@neynar/ui 0.1.1 → 0.1.2
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/components/ui/accordion.d.ts +1 -25
- package/dist/components/ui/accordion.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +240 -46
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert.d.ts +73 -11
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/aspect-ratio.d.ts +44 -10
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -1
- package/dist/components/ui/avatar.d.ts +117 -33
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/badge.d.ts +50 -71
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts +231 -49
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/button.d.ts +189 -71
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/calendar.d.ts +197 -40
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/card.d.ts +7 -22
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/carousel.d.ts +369 -99
- package/dist/components/ui/carousel.d.ts.map +1 -1
- package/dist/components/ui/chart.d.ts.map +1 -1
- package/dist/components/ui/checkbox.d.ts +110 -38
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/collapsible.d.ts +246 -61
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/combobox.d.ts +207 -159
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/command.d.ts +336 -67
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/container.d.ts +159 -64
- package/dist/components/ui/container.d.ts.map +1 -1
- package/dist/components/ui/context-menu.d.ts +321 -39
- package/dist/components/ui/context-menu.d.ts.map +1 -1
- package/dist/components/ui/date-picker.d.ts +113 -86
- package/dist/components/ui/date-picker.d.ts.map +1 -1
- package/dist/components/ui/dialog.d.ts +106 -25
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/drawer.d.ts +388 -59
- package/dist/components/ui/drawer.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +521 -74
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/empty-state.d.ts +148 -76
- package/dist/components/ui/empty-state.d.ts.map +1 -1
- package/dist/components/ui/hover-card.d.ts +253 -34
- package/dist/components/ui/hover-card.d.ts.map +1 -1
- package/dist/components/ui/input.d.ts +143 -44
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/label.d.ts +0 -8
- package/dist/components/ui/label.d.ts.map +1 -1
- package/dist/components/ui/menubar.d.ts +288 -46
- package/dist/components/ui/menubar.d.ts.map +1 -1
- package/dist/components/ui/navigation-menu.d.ts +444 -127
- package/dist/components/ui/navigation-menu.d.ts.map +1 -1
- package/dist/components/ui/pagination.d.ts +342 -66
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/popover.d.ts +0 -8
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/progress.d.ts +88 -30
- package/dist/components/ui/progress.d.ts.map +1 -1
- package/dist/components/ui/radio-group.d.ts +189 -45
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/resizable.d.ts +178 -62
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.d.ts +180 -21
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/select.d.ts +382 -60
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/separator.d.ts +52 -39
- package/dist/components/ui/separator.d.ts.map +1 -1
- package/dist/components/ui/sheet.d.ts +144 -27
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sidebar.d.ts +81 -31
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/skeleton.d.ts +94 -32
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/slider.d.ts +37 -31
- package/dist/components/ui/slider.d.ts.map +1 -1
- package/dist/components/ui/sonner.d.ts +280 -46
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/stack.d.ts +289 -148
- package/dist/components/ui/stack.d.ts.map +1 -1
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts +1 -2
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/container.stories.d.ts +2 -3
- package/dist/components/ui/stories/container.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/empty-state.stories.d.ts +2 -2
- package/dist/components/ui/stories/scroll-area.stories.d.ts +1 -2
- package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/stack.stories.d.ts +1 -1
- package/dist/components/ui/stories/text-field.stories.d.ts +7 -1
- package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -1
- package/dist/components/ui/switch.d.ts +44 -38
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/table.d.ts +33 -0
- package/dist/components/ui/table.d.ts.map +1 -1
- package/dist/components/ui/tabs.d.ts +4 -22
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/ui/text-field.d.ts +170 -84
- package/dist/components/ui/text-field.d.ts.map +1 -1
- package/dist/components/ui/textarea.d.ts +106 -29
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/theme-toggle.d.ts +190 -65
- package/dist/components/ui/theme-toggle.d.ts.map +1 -1
- package/dist/components/ui/theme.d.ts +107 -23
- package/dist/components/ui/theme.d.ts.map +1 -1
- package/dist/components/ui/toggle-group.d.ts +143 -67
- package/dist/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/components/ui/toggle.d.ts +118 -30
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/tooltip.d.ts +152 -28
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/typography.d.ts +452 -134
- package/dist/components/ui/typography.d.ts.map +1 -1
- package/dist/index.js +9388 -8281
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/llms.txt +173 -3
- package/package.json +5 -2
- package/src/components/ui/accordion.tsx +112 -27
- package/src/components/ui/alert-dialog.tsx +401 -46
- package/src/components/ui/alert.tsx +114 -11
- package/src/components/ui/aspect-ratio.tsx +69 -14
- package/src/components/ui/avatar.tsx +179 -33
- package/src/components/ui/badge.tsx +74 -75
- package/src/components/ui/breadcrumb.tsx +335 -50
- package/src/components/ui/button.tsx +198 -90
- package/src/components/ui/calendar.tsx +867 -43
- package/src/components/ui/card.tsx +140 -33
- package/src/components/ui/carousel.tsx +529 -98
- package/src/components/ui/chart.tsx +222 -1
- package/src/components/ui/checkbox.tsx +176 -38
- package/src/components/ui/collapsible.tsx +321 -67
- package/src/components/ui/combobox.tsx +284 -83
- package/src/components/ui/command.tsx +527 -67
- package/src/components/ui/container.tsx +217 -65
- package/src/components/ui/context-menu.tsx +716 -51
- package/src/components/ui/date-picker.tsx +228 -38
- package/src/components/ui/dialog.tsx +270 -33
- package/src/components/ui/drawer.tsx +546 -67
- package/src/components/ui/dropdown-menu.tsx +657 -74
- package/src/components/ui/empty-state.tsx +241 -82
- package/src/components/ui/hover-card.tsx +328 -39
- package/src/components/ui/input.tsx +207 -44
- package/src/components/ui/label.tsx +98 -8
- package/src/components/ui/menubar.tsx +587 -54
- package/src/components/ui/navigation-menu.tsx +557 -128
- package/src/components/ui/pagination.tsx +561 -79
- package/src/components/ui/popover.tsx +119 -8
- package/src/components/ui/progress.tsx +131 -29
- package/src/components/ui/radio-group.tsx +260 -51
- package/src/components/ui/resizable.tsx +289 -63
- package/src/components/ui/scroll-area.tsx +377 -66
- package/src/components/ui/select.tsx +545 -60
- package/src/components/ui/separator.tsx +146 -40
- package/src/components/ui/sheet.tsx +348 -31
- package/src/components/ui/sidebar.tsx +471 -29
- package/src/components/ui/skeleton.tsx +114 -32
- package/src/components/ui/slider.tsx +77 -31
- package/src/components/ui/sonner.tsx +574 -46
- package/src/components/ui/stack.tsx +423 -101
- package/src/components/ui/switch.tsx +78 -39
- package/src/components/ui/table.tsx +170 -4
- package/src/components/ui/tabs.tsx +108 -22
- package/src/components/ui/text-field.tsx +226 -81
- package/src/components/ui/textarea.tsx +180 -29
- package/src/components/ui/theme-toggle.tsx +313 -65
- package/src/components/ui/theme.tsx +117 -23
- package/src/components/ui/toggle-group.tsx +280 -69
- package/src/components/ui/toggle.tsx +124 -35
- package/src/components/ui/tooltip.tsx +239 -29
- package/src/components/ui/typography.tsx +1115 -165
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Popover } from "@/components/ui/popover";
|
|
1
3
|
/**
|
|
2
4
|
* Option structure for combobox items
|
|
3
5
|
*
|
|
@@ -28,111 +30,30 @@ export type ComboboxOption = {
|
|
|
28
30
|
*/
|
|
29
31
|
disabled?: boolean;
|
|
30
32
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Properties for the Combobox component
|
|
33
|
-
*
|
|
34
|
-
* Comprehensive configuration options for customizing combobox behavior,
|
|
35
|
-
* appearance, and interaction patterns.
|
|
36
|
-
*
|
|
37
|
-
* @since 1.0.0
|
|
38
|
-
*/
|
|
39
|
-
export type ComboboxProps = {
|
|
40
|
-
/**
|
|
41
|
-
* Array of options to display in the combobox dropdown
|
|
42
|
-
*
|
|
43
|
-
* Each option should have a unique value and descriptive label.
|
|
44
|
-
* Options can be disabled to prevent selection while remaining visible.
|
|
45
|
-
*/
|
|
46
|
-
options: ComboboxOption[];
|
|
47
|
-
/**
|
|
48
|
-
* Currently selected value from the options array
|
|
49
|
-
*
|
|
50
|
-
* Should match the value property of one of the provided options.
|
|
51
|
-
* Use with onValueChange for controlled component behavior.
|
|
52
|
-
*/
|
|
53
|
-
value?: string;
|
|
54
|
-
/**
|
|
55
|
-
* Callback fired when the selected value changes
|
|
56
|
-
*
|
|
57
|
-
* Receives the new selected value as a string. If the same option
|
|
58
|
-
* is clicked again, an empty string is passed to allow deselection.
|
|
59
|
-
*
|
|
60
|
-
* @param value - The newly selected option value or empty string for deselection
|
|
61
|
-
*/
|
|
62
|
-
onValueChange?: (value: string) => void;
|
|
63
|
-
/**
|
|
64
|
-
* Placeholder text displayed when no option is selected
|
|
65
|
-
*
|
|
66
|
-
* Should be descriptive and help users understand what they're selecting.
|
|
67
|
-
*
|
|
68
|
-
* @default "Select option..."
|
|
69
|
-
*/
|
|
70
|
-
placeholder?: string;
|
|
71
|
-
/**
|
|
72
|
-
* Text displayed when search query returns no matching options
|
|
73
|
-
*
|
|
74
|
-
* Customize this message to match your application's tone and context.
|
|
75
|
-
*
|
|
76
|
-
* @default "No option found."
|
|
77
|
-
*/
|
|
78
|
-
emptyText?: string;
|
|
79
|
-
/**
|
|
80
|
-
* Placeholder text for the search input field
|
|
81
|
-
*
|
|
82
|
-
* Should guide users on how to search effectively within your option set.
|
|
83
|
-
*
|
|
84
|
-
* @default "Search options..."
|
|
85
|
-
*/
|
|
86
|
-
searchPlaceholder?: string;
|
|
87
|
-
/**
|
|
88
|
-
* Additional CSS classes for the root container element
|
|
89
|
-
*
|
|
90
|
-
* Use for custom spacing, positioning, or layout adjustments.
|
|
91
|
-
*/
|
|
92
|
-
className?: string;
|
|
93
|
-
/**
|
|
94
|
-
* Whether the entire combobox is disabled
|
|
95
|
-
*
|
|
96
|
-
* When disabled, the trigger button cannot be clicked and the
|
|
97
|
-
* dropdown cannot be opened. Use individual option.disabled for
|
|
98
|
-
* granular control.
|
|
99
|
-
*
|
|
100
|
-
* @default false
|
|
101
|
-
*/
|
|
102
|
-
disabled?: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* Additional CSS classes for the trigger button
|
|
105
|
-
*
|
|
106
|
-
* Commonly used to control width (e.g., "w-[300px]"), styling variants,
|
|
107
|
-
* or responsive behavior. For best UX, consider matching popoverClassName width.
|
|
108
|
-
*
|
|
109
|
-
* @example "w-full sm:w-[280px] border-primary/50"
|
|
110
|
-
*/
|
|
111
|
-
buttonClassName?: string;
|
|
112
|
-
/**
|
|
113
|
-
* Additional CSS classes for the popover dropdown content
|
|
114
|
-
*
|
|
115
|
-
* Should typically match buttonClassName width for consistent alignment.
|
|
116
|
-
* Use to control positioning, width, and styling of the dropdown.
|
|
117
|
-
*
|
|
118
|
-
* @example "w-full sm:w-[280px] border-primary/50"
|
|
119
|
-
*/
|
|
120
|
-
popoverClassName?: string;
|
|
121
|
-
};
|
|
122
33
|
/**
|
|
123
34
|
* Searchable dropdown selection component with typeahead functionality
|
|
124
35
|
*
|
|
125
36
|
* A versatile combobox that combines a button trigger with a searchable dropdown list.
|
|
126
37
|
* Ideal for selecting from moderate to large lists of options where search functionality
|
|
127
|
-
* improves user experience. Built on
|
|
38
|
+
* improves user experience. Built on Radix UI Popover primitives for accessibility and
|
|
39
|
+
* CMDK for powerful command menu functionality with real-time filtering.
|
|
128
40
|
*
|
|
129
|
-
*
|
|
130
|
-
* -
|
|
131
|
-
* -
|
|
132
|
-
* -
|
|
133
|
-
* -
|
|
41
|
+
* **Technical Architecture:**
|
|
42
|
+
* - **Popover Container**: Radix UI Popover.Root provides modal/non-modal behavior, focus management, and positioning
|
|
43
|
+
* - **Trigger Button**: Uses Button component with proper ARIA attributes and visual states
|
|
44
|
+
* - **Command Menu**: CMDK provides keyboard navigation, filtering, and accessibility features
|
|
45
|
+
* - **State Management**: Controlled/uncontrolled modes with proper React patterns
|
|
134
46
|
*
|
|
135
|
-
*
|
|
47
|
+
* **Use Cases:**
|
|
48
|
+
* - Selecting from 10+ options that benefit from real-time filtering
|
|
49
|
+
* - User interfaces requiring quick option discovery through search
|
|
50
|
+
* - Forms needing better UX than basic select dropdowns
|
|
51
|
+
* - Applications with dynamic or large option sets
|
|
52
|
+
*
|
|
53
|
+
* **When Not to Use:**
|
|
54
|
+
* - Simple selection from few options (use Select component instead)
|
|
55
|
+
* - Multi-select scenarios (use Checkbox group or specialized multi-select)
|
|
56
|
+
* - Tree or hierarchical data (use TreeSelect or nested menus)
|
|
136
57
|
*
|
|
137
58
|
* @component
|
|
138
59
|
* @example
|
|
@@ -141,9 +62,9 @@ export type ComboboxProps = {
|
|
|
141
62
|
* const [framework, setFramework] = useState("")
|
|
142
63
|
*
|
|
143
64
|
* const frameworks = [
|
|
144
|
-
* { value: "
|
|
65
|
+
* { value: "nextjs", label: "Next.js" },
|
|
145
66
|
* { value: "remix", label: "Remix" },
|
|
146
|
-
* { value: "
|
|
67
|
+
* { value: "sveltekit", label: "SvelteKit" },
|
|
147
68
|
* { value: "nuxt", label: "Nuxt.js" },
|
|
148
69
|
* ]
|
|
149
70
|
*
|
|
@@ -154,11 +75,12 @@ export type ComboboxProps = {
|
|
|
154
75
|
* placeholder="Select framework..."
|
|
155
76
|
* searchPlaceholder="Search frameworks..."
|
|
156
77
|
* buttonClassName="w-[300px]"
|
|
78
|
+
* popoverClassName="w-[300px]"
|
|
157
79
|
* />
|
|
158
80
|
* ```
|
|
159
81
|
*
|
|
160
82
|
* @example
|
|
161
|
-
*
|
|
83
|
+
* Advanced combobox with disabled options and custom styling
|
|
162
84
|
* ```tsx
|
|
163
85
|
* const statusOptions = [
|
|
164
86
|
* { value: "active", label: "Active" },
|
|
@@ -170,101 +92,227 @@ export type ComboboxProps = {
|
|
|
170
92
|
* <Combobox
|
|
171
93
|
* options={statusOptions}
|
|
172
94
|
* value={status}
|
|
173
|
-
* onValueChange={
|
|
95
|
+
* onValueChange={(value) => {
|
|
96
|
+
* setStatus(value)
|
|
97
|
+
* // Track selection analytics
|
|
98
|
+
* analytics.track('status_selected', { value })
|
|
99
|
+
* }}
|
|
174
100
|
* placeholder="Select status..."
|
|
175
|
-
* emptyText="No status found."
|
|
101
|
+
* emptyText="No matching status found."
|
|
102
|
+
* searchPlaceholder="Filter statuses..."
|
|
176
103
|
* buttonClassName="w-[250px] border-primary/50"
|
|
177
104
|
* popoverClassName="w-[250px]"
|
|
105
|
+
* disabled={isLoading}
|
|
178
106
|
* />
|
|
179
107
|
* ```
|
|
180
108
|
*
|
|
181
109
|
* @example
|
|
182
|
-
* Form integration with validation and error
|
|
110
|
+
* Form integration with validation and error handling
|
|
183
111
|
* ```tsx
|
|
184
112
|
* import { Label } from "@/components/ui/label"
|
|
113
|
+
* import { useFormContext } from "react-hook-form"
|
|
185
114
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
115
|
+
* function CountryField() {
|
|
116
|
+
* const { register, setValue, watch, formState: { errors } } = useFormContext()
|
|
117
|
+
* const selectedCountry = watch("country")
|
|
118
|
+
*
|
|
119
|
+
* return (
|
|
120
|
+
* <div className="space-y-2">
|
|
121
|
+
* <Label htmlFor="country" className={errors.country ? "text-destructive" : ""}>
|
|
122
|
+
* Country {errors.country && "*"}
|
|
123
|
+
* </Label>
|
|
124
|
+
* <Combobox
|
|
125
|
+
* options={countries}
|
|
126
|
+
* value={selectedCountry}
|
|
127
|
+
* onValueChange={(value) => setValue("country", value, { shouldValidate: true })}
|
|
128
|
+
* placeholder="Choose your country..."
|
|
129
|
+
* searchPlaceholder="Type to search countries..."
|
|
130
|
+
* emptyText="Country not found. Try a different search."
|
|
131
|
+
* buttonClassName={cn(
|
|
132
|
+
* "w-full",
|
|
133
|
+
* errors.country && "border-destructive focus-visible:ring-destructive"
|
|
134
|
+
* )}
|
|
135
|
+
* popoverClassName="w-full"
|
|
136
|
+
* disabled={isSubmitting}
|
|
137
|
+
* />
|
|
138
|
+
* {errors.country && (
|
|
139
|
+
* <p className="text-sm text-destructive" role="alert">
|
|
140
|
+
* {errors.country.message}
|
|
141
|
+
* </p>
|
|
142
|
+
* )}
|
|
143
|
+
* </div>
|
|
144
|
+
* )
|
|
145
|
+
* }
|
|
206
146
|
* ```
|
|
207
147
|
*
|
|
208
148
|
* @example
|
|
209
|
-
* Responsive
|
|
149
|
+
* Responsive design with mobile optimization
|
|
210
150
|
* ```tsx
|
|
211
151
|
* <Combobox
|
|
212
|
-
* options={
|
|
213
|
-
* value={
|
|
214
|
-
* onValueChange={
|
|
215
|
-
* placeholder="
|
|
216
|
-
* searchPlaceholder="Search
|
|
217
|
-
*
|
|
218
|
-
*
|
|
152
|
+
* options={teamMembers}
|
|
153
|
+
* value={assignedTo}
|
|
154
|
+
* onValueChange={setAssignedTo}
|
|
155
|
+
* placeholder="Assign to team member..."
|
|
156
|
+
* searchPlaceholder="Search team members..."
|
|
157
|
+
* emptyText="No team members found."
|
|
158
|
+
* buttonClassName="w-full sm:w-[280px] md:w-[320px]"
|
|
159
|
+
* popoverClassName="w-full sm:w-[280px] md:w-[320px]"
|
|
160
|
+
* // Mobile: full width, Desktop: fixed width for consistent layout
|
|
219
161
|
* />
|
|
220
162
|
* ```
|
|
221
163
|
*
|
|
164
|
+
* @example
|
|
165
|
+
* Async data loading with loading states
|
|
166
|
+
* ```tsx
|
|
167
|
+
* function AsyncCombobox() {
|
|
168
|
+
* const [options, setOptions] = useState([])
|
|
169
|
+
* const [loading, setLoading] = useState(false)
|
|
170
|
+
* const [searchTerm, setSearchTerm] = useState("")
|
|
171
|
+
*
|
|
172
|
+
* // Debounced search effect
|
|
173
|
+
* useEffect(() => {
|
|
174
|
+
* if (!searchTerm) return
|
|
175
|
+
*
|
|
176
|
+
* const timeoutId = setTimeout(async () => {
|
|
177
|
+
* setLoading(true)
|
|
178
|
+
* try {
|
|
179
|
+
* const results = await searchAPI(searchTerm)
|
|
180
|
+
* setOptions(results)
|
|
181
|
+
* } finally {
|
|
182
|
+
* setLoading(false)
|
|
183
|
+
* }
|
|
184
|
+
* }, 300)
|
|
185
|
+
*
|
|
186
|
+
* return () => clearTimeout(timeoutId)
|
|
187
|
+
* }, [searchTerm])
|
|
188
|
+
*
|
|
189
|
+
* return (
|
|
190
|
+
* <Combobox
|
|
191
|
+
* options={options}
|
|
192
|
+
* value={selectedValue}
|
|
193
|
+
* onValueChange={setSelectedValue}
|
|
194
|
+
* placeholder={loading ? "Searching..." : "Search items..."}
|
|
195
|
+
* emptyText={loading ? "Loading..." : "No results found."}
|
|
196
|
+
* disabled={loading}
|
|
197
|
+
* />
|
|
198
|
+
* )
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
222
202
|
* @accessibility
|
|
223
203
|
*
|
|
224
|
-
* **ARIA Implementation:**
|
|
225
|
-
* -
|
|
226
|
-
* -
|
|
227
|
-
* -
|
|
228
|
-
* -
|
|
204
|
+
* **ARIA Implementation (WCAG 2.1 Level AA Compliant):**
|
|
205
|
+
* - **Combobox Role**: Trigger button uses `role="combobox"` with proper `aria-expanded` state
|
|
206
|
+
* - **Popup Association**: `aria-controls` links trigger to popup when visible
|
|
207
|
+
* - **Active Descendant**: `aria-activedescendant` manages focus within dropdown
|
|
208
|
+
* - **Popup Type**: `aria-haspopup="listbox"` indicates the nature of the popup
|
|
209
|
+
* - **Selection State**: `aria-selected` attributes on options indicate current selection
|
|
210
|
+
* - **Disabled State**: Proper `aria-disabled` attributes for unavailable options
|
|
229
211
|
*
|
|
230
|
-
* **Keyboard Navigation (W3C ARIA 1.2
|
|
231
|
-
* - **Tab**: Moves focus to/from combobox in
|
|
232
|
-
* - **
|
|
212
|
+
* **Keyboard Navigation (W3C ARIA 1.2 Combobox Pattern):**
|
|
213
|
+
* - **Tab**: Moves focus to/from combobox in natural tab order
|
|
214
|
+
* - **Space/Enter**: Opens/closes dropdown, selects focused option
|
|
215
|
+
* - **Down Arrow**: Opens dropdown (if closed) or moves to next option
|
|
233
216
|
* - **Up Arrow**: Moves to previous option (when dropdown is open)
|
|
234
|
-
* - **
|
|
235
|
-
* - **
|
|
236
|
-
* - **
|
|
217
|
+
* - **Home**: Moves to first option in list
|
|
218
|
+
* - **End**: Moves to last option in list
|
|
219
|
+
* - **Escape**: Closes dropdown and returns focus to trigger button
|
|
237
220
|
* - **Type-ahead**: Real-time filtering as user types in search input
|
|
221
|
+
* - **Character Keys**: When dropdown is closed, opens and starts filtering
|
|
238
222
|
*
|
|
239
223
|
* **Screen Reader Support:**
|
|
240
|
-
* -
|
|
241
|
-
* -
|
|
242
|
-
* -
|
|
243
|
-
* -
|
|
224
|
+
* - **Selection Announcements**: Current selection state announced on focus
|
|
225
|
+
* - **Option Count**: Number of available options announced when dropdown opens
|
|
226
|
+
* - **Filter Results**: Live announcements of filtered results count
|
|
227
|
+
* - **State Changes**: Open/close state changes announced appropriately
|
|
228
|
+
* - **Empty State**: Clear messaging when no options match search
|
|
229
|
+
* - **Disabled Feedback**: Disabled options announced and properly skipped
|
|
244
230
|
*
|
|
245
231
|
* **Focus Management:**
|
|
246
|
-
* - DOM focus remains on combobox trigger for screen reader compatibility
|
|
247
|
-
* - Visual
|
|
248
|
-
* - Search input automatically receives focus when dropdown opens
|
|
249
|
-
* - Focus returns to trigger when dropdown closes via Escape or selection
|
|
232
|
+
* - **Programmatic Focus**: DOM focus remains on combobox trigger for screen reader compatibility
|
|
233
|
+
* - **Visual Focus**: Options highlighted using `aria-activedescendant` pattern
|
|
234
|
+
* - **Auto Focus**: Search input automatically receives focus when dropdown opens
|
|
235
|
+
* - **Focus Return**: Focus returns to trigger when dropdown closes via Escape or selection
|
|
236
|
+
* - **Focus Trap**: Focus contained within popover when modal behavior is desired
|
|
250
237
|
*
|
|
251
238
|
* **Visual Accessibility:**
|
|
252
|
-
* -
|
|
253
|
-
* -
|
|
254
|
-
* -
|
|
255
|
-
* -
|
|
239
|
+
* - **Color Contrast**: All states meet WCAG AA contrast requirements (4.5:1)
|
|
240
|
+
* - **Focus Indicators**: Clear visual focus indicators for all interactive elements
|
|
241
|
+
* - **Hover States**: Distinct hover states for better interaction feedback
|
|
242
|
+
* - **Disabled State**: Visually distinct disabled options with reduced opacity
|
|
243
|
+
* - **Selection Indicator**: Check icon provides clear visual selection feedback
|
|
244
|
+
* - **High Contrast Mode**: Supports Windows High Contrast Mode
|
|
245
|
+
*
|
|
246
|
+
* **Touch and Mobile Accessibility:**
|
|
247
|
+
* - **Touch Targets**: Minimum 44px touch target size for mobile interactions
|
|
248
|
+
* - **Scroll Behavior**: Proper scroll support for long option lists
|
|
249
|
+
* - **Responsive Design**: Adapts to different screen sizes and orientations
|
|
250
|
+
* - **Mobile Navigation**: Touch-optimized selection and scrolling
|
|
256
251
|
*
|
|
257
252
|
* @performance
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* -
|
|
253
|
+
*
|
|
254
|
+
* **Optimization Strategies:**
|
|
255
|
+
* - **Efficient Filtering**: Single-pass option filtering with memoized results
|
|
256
|
+
* - **Minimal Re-renders**: Optimized state management prevents unnecessary renders
|
|
257
|
+
* - **Virtual Scrolling**: Built-in support for large datasets via CMDK virtualization
|
|
258
|
+
* - **Debounced Search**: Search input changes debounced to prevent excessive API calls
|
|
259
|
+
* - **Lazy Loading**: Options can be loaded asynchronously as needed
|
|
260
|
+
*
|
|
261
|
+
* **Memory Management:**
|
|
262
|
+
* - **Event Cleanup**: Proper cleanup of event listeners and timers
|
|
263
|
+
* - **Reference Management**: No memory leaks through proper ref handling
|
|
264
|
+
* - **Option Caching**: Previously loaded options cached for better performance
|
|
265
|
+
*
|
|
266
|
+
* @technical
|
|
267
|
+
*
|
|
268
|
+
* **Component Composition:**
|
|
269
|
+
* ```
|
|
270
|
+
* Combobox
|
|
271
|
+
* ├── Popover (Radix UI)
|
|
272
|
+
* │ ├── PopoverTrigger
|
|
273
|
+
* │ │ └── Button (outline variant)
|
|
274
|
+
* │ └── PopoverContent
|
|
275
|
+
* │ └── Command (CMDK)
|
|
276
|
+
* │ ├── CommandInput (search)
|
|
277
|
+
* │ └── CommandList
|
|
278
|
+
* │ ├── CommandEmpty
|
|
279
|
+
* │ └── CommandGroup
|
|
280
|
+
* │ └── CommandItem(s)
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* **State Flow:**
|
|
284
|
+
* 1. User clicks trigger → Popover opens → Command input focuses
|
|
285
|
+
* 2. User types → CMDK filters options → Results update
|
|
286
|
+
* 3. User navigates → aria-activedescendant updates → Visual focus moves
|
|
287
|
+
* 4. User selects → onValueChange fires → Popover closes → Focus returns
|
|
288
|
+
*
|
|
289
|
+
* **Event Handling:**
|
|
290
|
+
* - Popover manages open/close state and positioning
|
|
291
|
+
* - CMDK handles keyboard navigation and filtering
|
|
292
|
+
* - Button handles trigger interactions and ARIA states
|
|
293
|
+
* - Custom logic manages selection and deselection behavior
|
|
261
294
|
*
|
|
262
295
|
* @see {@link https://ui.shadcn.com/docs/components/combobox} shadcn/ui Combobox documentation
|
|
263
296
|
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/} W3C ARIA Combobox Pattern
|
|
297
|
+
* @see {@link https://www.radix-ui.com/primitives/docs/components/popover} Radix UI Popover API
|
|
298
|
+
* @see {@link https://cmdk.paco.me/} CMDK Command Menu documentation
|
|
264
299
|
* @see {@link Select} For simpler dropdowns without search functionality
|
|
265
300
|
* @see {@link Command} The underlying command menu component
|
|
266
301
|
* @see {@link Popover} The popover container component
|
|
302
|
+
* @see {@link Button} The trigger button component
|
|
267
303
|
* @since 1.0.0
|
|
268
304
|
*/
|
|
269
|
-
|
|
305
|
+
declare function Combobox({ options, value, onValueChange, placeholder, emptyText, searchPlaceholder, className, disabled, buttonClassName, popoverClassName, ...popoverProps }: {
|
|
306
|
+
options: ComboboxOption[];
|
|
307
|
+
value?: string;
|
|
308
|
+
onValueChange?: (value: string) => void;
|
|
309
|
+
placeholder?: string;
|
|
310
|
+
emptyText?: string;
|
|
311
|
+
searchPlaceholder?: string;
|
|
312
|
+
className?: string;
|
|
313
|
+
disabled?: boolean;
|
|
314
|
+
buttonClassName?: string;
|
|
315
|
+
popoverClassName?: string;
|
|
316
|
+
} & Omit<React.ComponentProps<typeof Popover>, "children">): import("react/jsx-runtime").JSX.Element;
|
|
317
|
+
export { Combobox };
|
|
270
318
|
//# sourceMappingURL=combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../src/components/ui/combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../src/components/ui/combobox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,OAAO,EACL,OAAO,EAGR,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAiKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+QG;AACH,iBAAS,QAAQ,CAAC,EAChB,OAAO,EACP,KAAK,EACL,aAAa,EACb,WAAgC,EAChC,SAA8B,EAC9B,iBAAuC,EACvC,SAAS,EACT,QAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,GAAG,YAAY,EAChB,EAAE;IACD,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,EAAE,UAAU,CAAC,2CA+DzD;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|