@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.
Files changed (173) hide show
  1. package/dist/components/ui/accordion.d.ts +1 -25
  2. package/dist/components/ui/accordion.d.ts.map +1 -1
  3. package/dist/components/ui/alert-dialog.d.ts +240 -46
  4. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  5. package/dist/components/ui/alert.d.ts +73 -11
  6. package/dist/components/ui/alert.d.ts.map +1 -1
  7. package/dist/components/ui/aspect-ratio.d.ts +44 -10
  8. package/dist/components/ui/aspect-ratio.d.ts.map +1 -1
  9. package/dist/components/ui/avatar.d.ts +117 -33
  10. package/dist/components/ui/avatar.d.ts.map +1 -1
  11. package/dist/components/ui/badge.d.ts +50 -71
  12. package/dist/components/ui/badge.d.ts.map +1 -1
  13. package/dist/components/ui/breadcrumb.d.ts +231 -49
  14. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  15. package/dist/components/ui/button.d.ts +189 -71
  16. package/dist/components/ui/button.d.ts.map +1 -1
  17. package/dist/components/ui/calendar.d.ts +197 -40
  18. package/dist/components/ui/calendar.d.ts.map +1 -1
  19. package/dist/components/ui/card.d.ts +7 -22
  20. package/dist/components/ui/card.d.ts.map +1 -1
  21. package/dist/components/ui/carousel.d.ts +369 -99
  22. package/dist/components/ui/carousel.d.ts.map +1 -1
  23. package/dist/components/ui/chart.d.ts.map +1 -1
  24. package/dist/components/ui/checkbox.d.ts +110 -38
  25. package/dist/components/ui/checkbox.d.ts.map +1 -1
  26. package/dist/components/ui/collapsible.d.ts +246 -61
  27. package/dist/components/ui/collapsible.d.ts.map +1 -1
  28. package/dist/components/ui/combobox.d.ts +207 -159
  29. package/dist/components/ui/combobox.d.ts.map +1 -1
  30. package/dist/components/ui/command.d.ts +336 -67
  31. package/dist/components/ui/command.d.ts.map +1 -1
  32. package/dist/components/ui/container.d.ts +159 -64
  33. package/dist/components/ui/container.d.ts.map +1 -1
  34. package/dist/components/ui/context-menu.d.ts +321 -39
  35. package/dist/components/ui/context-menu.d.ts.map +1 -1
  36. package/dist/components/ui/date-picker.d.ts +113 -86
  37. package/dist/components/ui/date-picker.d.ts.map +1 -1
  38. package/dist/components/ui/dialog.d.ts +106 -25
  39. package/dist/components/ui/dialog.d.ts.map +1 -1
  40. package/dist/components/ui/drawer.d.ts +388 -59
  41. package/dist/components/ui/drawer.d.ts.map +1 -1
  42. package/dist/components/ui/dropdown-menu.d.ts +521 -74
  43. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  44. package/dist/components/ui/empty-state.d.ts +148 -76
  45. package/dist/components/ui/empty-state.d.ts.map +1 -1
  46. package/dist/components/ui/hover-card.d.ts +253 -34
  47. package/dist/components/ui/hover-card.d.ts.map +1 -1
  48. package/dist/components/ui/input.d.ts +143 -44
  49. package/dist/components/ui/input.d.ts.map +1 -1
  50. package/dist/components/ui/label.d.ts +0 -8
  51. package/dist/components/ui/label.d.ts.map +1 -1
  52. package/dist/components/ui/menubar.d.ts +288 -46
  53. package/dist/components/ui/menubar.d.ts.map +1 -1
  54. package/dist/components/ui/navigation-menu.d.ts +444 -127
  55. package/dist/components/ui/navigation-menu.d.ts.map +1 -1
  56. package/dist/components/ui/pagination.d.ts +342 -66
  57. package/dist/components/ui/pagination.d.ts.map +1 -1
  58. package/dist/components/ui/popover.d.ts +0 -8
  59. package/dist/components/ui/popover.d.ts.map +1 -1
  60. package/dist/components/ui/progress.d.ts +88 -30
  61. package/dist/components/ui/progress.d.ts.map +1 -1
  62. package/dist/components/ui/radio-group.d.ts +189 -45
  63. package/dist/components/ui/radio-group.d.ts.map +1 -1
  64. package/dist/components/ui/resizable.d.ts +178 -62
  65. package/dist/components/ui/resizable.d.ts.map +1 -1
  66. package/dist/components/ui/scroll-area.d.ts +180 -21
  67. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  68. package/dist/components/ui/select.d.ts +382 -60
  69. package/dist/components/ui/select.d.ts.map +1 -1
  70. package/dist/components/ui/separator.d.ts +52 -39
  71. package/dist/components/ui/separator.d.ts.map +1 -1
  72. package/dist/components/ui/sheet.d.ts +144 -27
  73. package/dist/components/ui/sheet.d.ts.map +1 -1
  74. package/dist/components/ui/sidebar.d.ts +81 -31
  75. package/dist/components/ui/sidebar.d.ts.map +1 -1
  76. package/dist/components/ui/skeleton.d.ts +94 -32
  77. package/dist/components/ui/skeleton.d.ts.map +1 -1
  78. package/dist/components/ui/slider.d.ts +37 -31
  79. package/dist/components/ui/slider.d.ts.map +1 -1
  80. package/dist/components/ui/sonner.d.ts +280 -46
  81. package/dist/components/ui/sonner.d.ts.map +1 -1
  82. package/dist/components/ui/stack.d.ts +289 -148
  83. package/dist/components/ui/stack.d.ts.map +1 -1
  84. package/dist/components/ui/stories/aspect-ratio.stories.d.ts +1 -2
  85. package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -1
  86. package/dist/components/ui/stories/container.stories.d.ts +2 -3
  87. package/dist/components/ui/stories/container.stories.d.ts.map +1 -1
  88. package/dist/components/ui/stories/empty-state.stories.d.ts +2 -2
  89. package/dist/components/ui/stories/scroll-area.stories.d.ts +1 -2
  90. package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -1
  91. package/dist/components/ui/stories/stack.stories.d.ts +1 -1
  92. package/dist/components/ui/stories/text-field.stories.d.ts +7 -1
  93. package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -1
  94. package/dist/components/ui/switch.d.ts +44 -38
  95. package/dist/components/ui/switch.d.ts.map +1 -1
  96. package/dist/components/ui/table.d.ts +33 -0
  97. package/dist/components/ui/table.d.ts.map +1 -1
  98. package/dist/components/ui/tabs.d.ts +4 -22
  99. package/dist/components/ui/tabs.d.ts.map +1 -1
  100. package/dist/components/ui/text-field.d.ts +170 -84
  101. package/dist/components/ui/text-field.d.ts.map +1 -1
  102. package/dist/components/ui/textarea.d.ts +106 -29
  103. package/dist/components/ui/textarea.d.ts.map +1 -1
  104. package/dist/components/ui/theme-toggle.d.ts +190 -65
  105. package/dist/components/ui/theme-toggle.d.ts.map +1 -1
  106. package/dist/components/ui/theme.d.ts +107 -23
  107. package/dist/components/ui/theme.d.ts.map +1 -1
  108. package/dist/components/ui/toggle-group.d.ts +143 -67
  109. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  110. package/dist/components/ui/toggle.d.ts +118 -30
  111. package/dist/components/ui/toggle.d.ts.map +1 -1
  112. package/dist/components/ui/tooltip.d.ts +152 -28
  113. package/dist/components/ui/tooltip.d.ts.map +1 -1
  114. package/dist/components/ui/typography.d.ts +452 -134
  115. package/dist/components/ui/typography.d.ts.map +1 -1
  116. package/dist/index.js +9388 -8281
  117. package/dist/index.js.map +1 -1
  118. package/dist/tsconfig.tsbuildinfo +1 -1
  119. package/llms.txt +173 -3
  120. package/package.json +5 -2
  121. package/src/components/ui/accordion.tsx +112 -27
  122. package/src/components/ui/alert-dialog.tsx +401 -46
  123. package/src/components/ui/alert.tsx +114 -11
  124. package/src/components/ui/aspect-ratio.tsx +69 -14
  125. package/src/components/ui/avatar.tsx +179 -33
  126. package/src/components/ui/badge.tsx +74 -75
  127. package/src/components/ui/breadcrumb.tsx +335 -50
  128. package/src/components/ui/button.tsx +198 -90
  129. package/src/components/ui/calendar.tsx +867 -43
  130. package/src/components/ui/card.tsx +140 -33
  131. package/src/components/ui/carousel.tsx +529 -98
  132. package/src/components/ui/chart.tsx +222 -1
  133. package/src/components/ui/checkbox.tsx +176 -38
  134. package/src/components/ui/collapsible.tsx +321 -67
  135. package/src/components/ui/combobox.tsx +284 -83
  136. package/src/components/ui/command.tsx +527 -67
  137. package/src/components/ui/container.tsx +217 -65
  138. package/src/components/ui/context-menu.tsx +716 -51
  139. package/src/components/ui/date-picker.tsx +228 -38
  140. package/src/components/ui/dialog.tsx +270 -33
  141. package/src/components/ui/drawer.tsx +546 -67
  142. package/src/components/ui/dropdown-menu.tsx +657 -74
  143. package/src/components/ui/empty-state.tsx +241 -82
  144. package/src/components/ui/hover-card.tsx +328 -39
  145. package/src/components/ui/input.tsx +207 -44
  146. package/src/components/ui/label.tsx +98 -8
  147. package/src/components/ui/menubar.tsx +587 -54
  148. package/src/components/ui/navigation-menu.tsx +557 -128
  149. package/src/components/ui/pagination.tsx +561 -79
  150. package/src/components/ui/popover.tsx +119 -8
  151. package/src/components/ui/progress.tsx +131 -29
  152. package/src/components/ui/radio-group.tsx +260 -51
  153. package/src/components/ui/resizable.tsx +289 -63
  154. package/src/components/ui/scroll-area.tsx +377 -66
  155. package/src/components/ui/select.tsx +545 -60
  156. package/src/components/ui/separator.tsx +146 -40
  157. package/src/components/ui/sheet.tsx +348 -31
  158. package/src/components/ui/sidebar.tsx +471 -29
  159. package/src/components/ui/skeleton.tsx +114 -32
  160. package/src/components/ui/slider.tsx +77 -31
  161. package/src/components/ui/sonner.tsx +574 -46
  162. package/src/components/ui/stack.tsx +423 -101
  163. package/src/components/ui/switch.tsx +78 -39
  164. package/src/components/ui/table.tsx +170 -4
  165. package/src/components/ui/tabs.tsx +108 -22
  166. package/src/components/ui/text-field.tsx +226 -81
  167. package/src/components/ui/textarea.tsx +180 -29
  168. package/src/components/ui/theme-toggle.tsx +313 -65
  169. package/src/components/ui/theme.tsx +117 -23
  170. package/src/components/ui/toggle-group.tsx +280 -69
  171. package/src/components/ui/toggle.tsx +124 -35
  172. package/src/components/ui/tooltip.tsx +239 -29
  173. package/src/components/ui/typography.tsx +1115 -165
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { Command as CommandPrimitive } from "cmdk";
2
+ import { Command as CommandPrimitive, useCommandState } from "cmdk";
3
3
  import { SearchIcon } from "lucide-react";
4
4
 
5
5
  import { cn } from "@/lib/utils";
@@ -11,6 +11,186 @@ import {
11
11
  DialogTitle,
12
12
  } from "@/components/ui/dialog";
13
13
 
14
+ // ============================================================================
15
+ // CMDK HOOK EXPORTS
16
+ // ============================================================================
17
+
18
+ // useCommandState is now imported directly from 'cmdk' and available for use
19
+
20
+ // ============================================================================
21
+ // DOCUMENTATION-ONLY PROP TYPES (Internal - not exported)
22
+ // ============================================================================
23
+
24
+ /**
25
+ * Props for Command component (Documentation only - NOT used in component implementation)
26
+ * These types are for documentation generation and should not replace CMDK inferred types
27
+ */
28
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
+ type CommandDocsProps = {
30
+ /** Accessibility label for the command menu - helps identify the menu purpose for screen readers */
31
+ label?: string;
32
+ /** Whether to filter and sort items automatically based on search input @default true */
33
+ shouldFilter?: boolean;
34
+ /** Custom filter function for ranking items based on search relevance - return 0 to hide, 1+ to show and rank */
35
+ filter?: (value: string, search: string, keywords?: string[]) => number;
36
+ /** Current selected item value for controlled usage - useful for managing selection state externally */
37
+ value?: string;
38
+ /** Callback fired when the selected item changes - receives the new selected value */
39
+ onValueChange?: (value: string) => void;
40
+ /** Whether to enable circular navigation with arrow keys through all items @default false */
41
+ loop?: boolean;
42
+ /** Whether to disable mouse/pointer selection and only allow keyboard navigation @default true */
43
+ disablePointerSelection?: boolean;
44
+ /** Whether to enable Vim-style keybindings for navigation (j/k for up/down) @default false */
45
+ vimBindings?: boolean;
46
+ /** Additional CSS classes for styling customization */
47
+ className?: string;
48
+ /** Child components - typically CommandInput, CommandList with CommandItems */
49
+ children?: React.ReactNode;
50
+ } & React.HTMLAttributes<HTMLDivElement>;
51
+
52
+ /**
53
+ * Props for CommandDialog component (Documentation only - NOT used in component implementation)
54
+ * These types are for documentation generation and should not replace Dialog + Command inferred types
55
+ */
56
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
+ type CommandDialogDocsProps = {
58
+ /** Whether the dialog is open - controls visibility state */
59
+ open?: boolean;
60
+ /** Callback when dialog open state changes - useful for managing dialog state */
61
+ onOpenChange?: (open: boolean) => void;
62
+ /** The title announced to screen readers for accessibility @default "Command Palette" */
63
+ title?: string;
64
+ /** The description announced to screen readers explaining dialog purpose @default "Search for a command to run..." */
65
+ description?: string;
66
+ /** Whether to show the X close button in the dialog header @default true */
67
+ showCloseButton?: boolean;
68
+ /** Portal container element for rendering the dialog - allows custom positioning */
69
+ container?: HTMLElement | null;
70
+ /** Additional CSS classes for dialog content styling */
71
+ className?: string;
72
+ /** Child components - typically Command with CommandInput and CommandList */
73
+ children?: React.ReactNode;
74
+ };
75
+
76
+ /**
77
+ * Props for CommandInput component (Documentation only - NOT used in component implementation)
78
+ * These types are for documentation generation and should not replace CMDK inferred types
79
+ */
80
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
+ type CommandInputDocsProps = {
82
+ /** Current search input value for controlled input behavior */
83
+ value?: string;
84
+ /** Callback fired when input value changes during typing - receives the new search string */
85
+ onValueChange?: (search: string) => void;
86
+ /** Placeholder text displayed when input is empty - helps users understand purpose */
87
+ placeholder?: string;
88
+ /** Whether the input is disabled and cannot receive focus @default false */
89
+ disabled?: boolean;
90
+ /** Whether to automatically focus the input when rendered @default false */
91
+ autoFocus?: boolean;
92
+ /** Additional CSS classes for input styling */
93
+ className?: string;
94
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "onChange">;
95
+
96
+ /**
97
+ * Props for CommandList component (Documentation only - NOT used in component implementation)
98
+ * These types are for documentation generation and should not replace CMDK inferred types
99
+ */
100
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
101
+ type CommandListDocsProps = {
102
+ /** Additional CSS classes for list container styling - useful for height and scroll behavior */
103
+ className?: string;
104
+ /** Child components - typically CommandEmpty, CommandGroup, and CommandItem components */
105
+ children?: React.ReactNode;
106
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "role">;
107
+
108
+ /**
109
+ * Props for CommandEmpty component (Documentation only - NOT used in component implementation)
110
+ * These types are for documentation generation and should not replace CMDK inferred types
111
+ */
112
+ type CommandEmptyDocsProps = {
113
+ /** Additional CSS classes for empty state styling */
114
+ className?: string;
115
+ /** Content to display when no items match the search - can be text or custom components */
116
+ children?: React.ReactNode;
117
+ } & React.HTMLAttributes<HTMLDivElement>;
118
+
119
+ /**
120
+ * Props for CommandGroup component (Documentation only - NOT used in component implementation)
121
+ * These types are for documentation generation and should not replace CMDK inferred types
122
+ */
123
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
124
+ type CommandGroupDocsProps = {
125
+ /** The group heading displayed above items - can be text or a React component */
126
+ heading?: React.ReactNode;
127
+ /** Group value identifier for programmatic control and filtering */
128
+ value?: string;
129
+ /** Whether to always render the group even when all items are filtered out @default false */
130
+ forceMount?: boolean;
131
+ /** Additional CSS classes for group styling */
132
+ className?: string;
133
+ /** Child components - typically CommandItem components */
134
+ children?: React.ReactNode;
135
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "role">;
136
+
137
+ /**
138
+ * Props for CommandSeparator component (Documentation only - NOT used in component implementation)
139
+ * These types are for documentation generation and should not replace CMDK inferred types
140
+ */
141
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
142
+ type CommandSeparatorDocsProps = {
143
+ /** Whether to always render the separator regardless of surrounding content visibility @default false */
144
+ alwaysRender?: boolean;
145
+ /** Additional CSS classes for separator styling */
146
+ className?: string;
147
+ } & React.HTMLAttributes<HTMLDivElement>;
148
+
149
+ /**
150
+ * Props for CommandItem component (Documentation only - NOT used in component implementation)
151
+ * These types are for documentation generation and should not replace CMDK inferred types
152
+ */
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
+ type CommandItemDocsProps = {
155
+ /** Unique item value used for filtering and selection - defaults to text content if not provided */
156
+ value?: string;
157
+ /** Additional keywords for improved search matching without affecting display */
158
+ keywords?: string[];
159
+ /** Whether the item is disabled and cannot be selected @default false */
160
+ disabled?: boolean;
161
+ /** Callback fired when item is selected via Enter key or mouse click - receives the item value */
162
+ onSelect?: (value: string) => void;
163
+ /** Whether to always render the item even when it doesn't match the search filter @default false */
164
+ forceMount?: boolean;
165
+ /** Additional CSS classes for item styling */
166
+ className?: string;
167
+ /** Child content - typically text, icons, and CommandShortcut components */
168
+ children?: React.ReactNode;
169
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">;
170
+
171
+ /**
172
+ * Props for CommandShortcut component (Documentation only - NOT used in component implementation)
173
+ * These types are for documentation generation and should not replace standard span props
174
+ */
175
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
176
+ type CommandShortcutDocsProps = {
177
+ /** Additional CSS classes for shortcut styling */
178
+ className?: string;
179
+ /** Keyboard shortcut text content - typically key combinations like "⌘K" or "Ctrl+N" */
180
+ children?: React.ReactNode;
181
+ } & React.HTMLAttributes<HTMLSpanElement>;
182
+
183
+ /**
184
+ * Props for CommandLoading component (Documentation only - NOT used in component implementation)
185
+ * These types are for documentation generation and extend CommandEmpty props
186
+ */
187
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
188
+ type CommandLoadingDocsProps = CommandEmptyDocsProps;
189
+
190
+ // ============================================================================
191
+ // COMPONENTS
192
+ // ============================================================================
193
+
14
194
  /**
15
195
  * Command - A fast, composable command menu for search and navigation
16
196
  *
@@ -18,11 +198,23 @@ import {
18
198
  * interface with search, filtering, and keyboard navigation. Perfect for creating
19
199
  * search interfaces, command palettes, and action menus with full accessibility support.
20
200
  *
21
- * @component
201
+ * Features advanced filtering with fuzzy search, keyboard shortcuts, nested navigation,
202
+ * asynchronous loading states, and comprehensive accessibility patterns.
203
+ *
204
+ * @param label - Accessibility label for screen readers and assistive technology
205
+ * @param shouldFilter - Controls automatic filtering and sorting @default true
206
+ * @param filter - Custom filter function for ranking items based on search
207
+ * @param value - Currently selected item value for controlled usage
208
+ * @param onValueChange - Callback fired when selected item changes
209
+ * @param loop - Enables circular navigation through items @default false
210
+ * @param disablePointerSelection - Prevents mouse selection behavior @default true
211
+ * @param vimBindings - Enables Vim-style navigation keybindings @default false
212
+ * @param className - Additional CSS classes
213
+ *
22
214
  * @example
23
215
  * ```tsx
24
216
  * // Basic command menu
25
- * <Command>
217
+ * <Command label="Main Menu">
26
218
  * <CommandInput placeholder="Type a command or search..." />
27
219
  * <CommandList>
28
220
  * <CommandEmpty>No results found.</CommandEmpty>
@@ -37,6 +229,34 @@ import {
37
229
  *
38
230
  * @example
39
231
  * ```tsx
232
+ * // Controlled command menu with custom filtering
233
+ * const [value, setValue] = useState('');
234
+ * const [search, setSearch] = useState('');
235
+ *
236
+ * <Command
237
+ * value={value}
238
+ * onValueChange={setValue}
239
+ * filter={(value, search, keywords) => {
240
+ * const extended = value + ' ' + (keywords?.join(' ') || '');
241
+ * return extended.toLowerCase().includes(search.toLowerCase()) ? 1 : 0;
242
+ * }}
243
+ * loop
244
+ * >
245
+ * <CommandInput
246
+ * value={search}
247
+ * onValueChange={setSearch}
248
+ * placeholder="Search..."
249
+ * />
250
+ * <CommandList>
251
+ * <CommandItem value="apple" keywords={['fruit', 'red']}>
252
+ * Apple
253
+ * </CommandItem>
254
+ * </CommandList>
255
+ * </Command>
256
+ * ```
257
+ *
258
+ * @example
259
+ * ```tsx
40
260
  * // Command dialog with keyboard shortcut
41
261
  * const [open, setOpen] = useState(false);
42
262
  *
@@ -65,17 +285,20 @@ import {
65
285
  * ```
66
286
  *
67
287
  * @accessibility
68
- * - Full keyboard navigation with arrow keys and Home/End
69
- * - Type-ahead search and filtering
288
+ * - Full keyboard navigation with Arrow keys, Home/End, Page Up/Down
289
+ * - Type-ahead search with live region announcements
70
290
  * - Enter key to select items, Escape to close dialogs
71
- * - Screen reader announcements for filtered results
72
- * - ARIA roles following command palette pattern
73
- * - Focus management and trapping in dialog mode
291
+ * - Screen reader announcements for filtered results and state changes
292
+ * - ARIA roles following WAI-ARIA command palette patterns
293
+ * - Focus management with proper tab trapping in dialog mode
294
+ * - Support for disabled items and groups
74
295
  *
296
+ * @see {@link https://cmdk.paco.me} Official CMDK documentation
75
297
  * @see {@link https://ui.shadcn.com/docs/components/command} for design patterns
76
298
  * @see {@link CommandDialog} - For modal command palette
77
299
  * @see {@link CommandInput} - Search input component
78
300
  * @see {@link CommandList} - Scrollable results container
301
+ * @see {@link useCommandState} - Hook for accessing command state
79
302
  * @since 1.0.0
80
303
  */
81
304
  function Command({
@@ -98,10 +321,20 @@ function Command({
98
321
  * CommandDialog - Modal dialog wrapper for command menu
99
322
  *
100
323
  * Presents the command menu in a modal dialog overlay, perfect for application-wide
101
- * command palettes triggered by keyboard shortcuts like Cmd+K. Includes proper
102
- * focus management and accessibility features.
324
+ * command palettes triggered by keyboard shortcuts like Cmd+K or Ctrl+K. Built on
325
+ * Radix UI Dialog with proper focus management, portal rendering, and accessibility.
326
+ *
327
+ * The dialog automatically handles focus trapping, scroll locking, and provides
328
+ * screen reader announcements. Use `container` prop to specify custom portal target.
329
+ *
330
+ * @param open - Controls dialog visibility state
331
+ * @param onOpenChange - Callback when dialog open state changes
332
+ * @param title - Screen reader accessible title @default "Command Palette"
333
+ * @param description - Screen reader description @default "Search for a command to run..."
334
+ * @param showCloseButton - Whether to show the X close button @default true
335
+ * @param container - Custom portal container element for rendering
336
+ * @param className - Additional CSS classes for dialog content
103
337
  *
104
- * @component
105
338
  * @example
106
339
  * ```tsx
107
340
  * // Command palette dialog with keyboard shortcut
@@ -129,16 +362,38 @@ function Command({
129
362
  * </CommandDialog>
130
363
  * ```
131
364
  *
132
- * @param title - Screen reader title for the dialog
133
- * @param description - Screen reader description for the dialog
134
- * @param showCloseButton - Whether to show the close button
365
+ * @example
366
+ * ```tsx
367
+ * // Custom portal container
368
+ * const containerRef = useRef<HTMLDivElement>(null);
369
+ *
370
+ * <>
371
+ * <CommandDialog
372
+ * open={open}
373
+ * onOpenChange={setOpen}
374
+ * container={containerRef.current}
375
+ * title="Quick Actions"
376
+ * description="Find and run commands quickly"
377
+ * >
378
+ * <CommandInput />
379
+ * <CommandList>
380
+ * <CommandItem>Action 1</CommandItem>
381
+ * </CommandList>
382
+ * </CommandDialog>
383
+ * <div ref={containerRef} />
384
+ * </>
385
+ * ```
135
386
  *
136
387
  * @accessibility
137
- * - Focus trapped within dialog when open
388
+ * - Focus trapped within dialog when open with proper restoration
138
389
  * - Escape key closes the dialog
390
+ * - Click outside or close button dismisses dialog
139
391
  * - Screen reader announcements with title and description
140
392
  * - Preserves all command menu keyboard navigation
393
+ * - Modal backdrop prevents interaction with background content
141
394
  *
395
+ * @see {@link Dialog} - Base dialog component
396
+ * @see {@link Command} - Command menu component
142
397
  * @since 1.0.0
143
398
  */
144
399
  function CommandDialog({
@@ -148,27 +403,13 @@ function CommandDialog({
148
403
  className,
149
404
  showCloseButton = true,
150
405
  ...props
151
- }: React.ComponentProps<typeof Dialog> & {
152
- /**
153
- * The title for screen readers
154
- * @default "Command Palette"
155
- */
406
+ }: {
156
407
  title?: string;
157
- /**
158
- * The description for screen readers
159
- * @default "Search for a command to run..."
160
- */
161
408
  description?: string;
162
- /**
163
- * Additional CSS classes
164
- */
409
+ children?: React.ReactNode;
165
410
  className?: string;
166
- /**
167
- * Whether to show the close button
168
- * @default true
169
- */
170
411
  showCloseButton?: boolean;
171
- }) {
412
+ } & React.ComponentProps<typeof Dialog>) {
172
413
  return (
173
414
  <Dialog {...props}>
174
415
  <DialogHeader className="sr-only">
@@ -179,7 +420,7 @@ function CommandDialog({
179
420
  className={cn("overflow-hidden p-0", className)}
180
421
  showCloseButton={showCloseButton}
181
422
  >
182
- <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
423
+ <Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[data-slot=command-input-wrapper]]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
183
424
  {children}
184
425
  </Command>
185
426
  </DialogContent>
@@ -191,11 +432,23 @@ function CommandDialog({
191
432
  * CommandInput - Search input for command menu filtering
192
433
  *
193
434
  * Provides a search input with search icon for filtering command menu items.
194
- * Automatically filters items as the user types and handles focus management.
435
+ * Automatically filters items as the user types and triggers live region announcements
436
+ * for screen readers. Supports both controlled and uncontrolled usage patterns.
437
+ *
438
+ * The input integrates with the Command context to provide real-time filtering,
439
+ * search highlighting, and result announcements. Includes built-in search icon
440
+ * and proper focus management.
441
+ *
442
+ * @param value - Controlled input value for managing search state externally
443
+ * @param onValueChange - Callback fired when input value changes
444
+ * @param placeholder - Placeholder text displayed when input is empty
445
+ * @param disabled - Disables the input and prevents interaction @default false
446
+ * @param autoFocus - Auto-focus the input when rendered @default false
447
+ * @param className - Additional CSS classes
195
448
  *
196
- * @component
197
449
  * @example
198
450
  * ```tsx
451
+ * // Basic uncontrolled input
199
452
  * <Command>
200
453
  * <CommandInput placeholder="Search commands..." />
201
454
  * <CommandList>...</CommandList>
@@ -204,18 +457,32 @@ function CommandDialog({
204
457
  *
205
458
  * @example
206
459
  * ```tsx
207
- * // Controlled input with state
460
+ * // Controlled input with external state
461
+ * const [search, setSearch] = useState('');
462
+ *
208
463
  * <CommandInput
464
+ * value={search}
465
+ * onValueChange={setSearch}
209
466
  * placeholder="Type a command or search..."
210
- * value={searchValue}
211
- * onValueChange={setSearchValue}
467
+ * autoFocus
468
+ * />
469
+ * ```
470
+ *
471
+ * @example
472
+ * ```tsx
473
+ * // With disabled state
474
+ * <CommandInput
475
+ * placeholder="Loading..."
476
+ * disabled={isLoading}
212
477
  * />
213
478
  * ```
214
479
  *
215
480
  * @accessibility
216
- * - Auto-focuses when command menu opens
217
- * - Live region announces result count to screen readers
481
+ * - Auto-focuses when command menu opens (if autoFocus is true)
482
+ * - Live region announces result count changes to screen readers
218
483
  * - Supports all standard input accessibility features
484
+ * - Proper labeling and description associations
485
+ * - Keyboard navigation integration with command list
219
486
  *
220
487
  * @since 1.0.0
221
488
  */
@@ -244,10 +511,15 @@ function CommandInput({
244
511
  /**
245
512
  * CommandList - Scrollable container for command items
246
513
  *
247
- * Contains and manages the list of command items with automatic scrolling
248
- * and overflow handling. Manages keyboard navigation and selection state.
514
+ * Contains and manages the list of command items with automatic scrolling,
515
+ * overflow handling, and dynamic height calculation. Provides the scrollable
516
+ * viewport for command items and groups with proper keyboard navigation.
517
+ *
518
+ * Uses CSS custom property `--cmdk-list-height` for smooth height transitions
519
+ * when items are filtered. Supports scroll padding for better item visibility.
520
+ *
521
+ * @param className - Additional CSS classes
249
522
  *
250
- * @component
251
523
  * @example
252
524
  * ```tsx
253
525
  * <CommandList>
@@ -259,10 +531,25 @@ function CommandInput({
259
531
  * </CommandList>
260
532
  * ```
261
533
  *
534
+ * @example
535
+ * ```tsx
536
+ * // With custom height and scroll behavior
537
+ * <CommandList className="max-h-[400px] scroll-py-2">
538
+ * {items.map((item) => (
539
+ * <CommandItem key={item.id} value={item.id}>
540
+ * {item.name}
541
+ * </CommandItem>
542
+ * ))}
543
+ * </CommandList>
544
+ * ```
545
+ *
262
546
  * @accessibility
263
547
  * - Scrollable with keyboard arrow navigation
264
548
  * - Maintains focus within list boundaries
265
- * - Supports Home/End key navigation
549
+ * - Supports Home/End key navigation to first/last items
550
+ * - Page Up/Page Down for faster scrolling
551
+ * - Proper ARIA role as listbox
552
+ * - Screen reader announcements for list changes
266
553
  *
267
554
  * @since 1.0.0
268
555
  */
@@ -286,9 +573,14 @@ function CommandList({
286
573
  * CommandEmpty - Empty state message for command menu
287
574
  *
288
575
  * Displays when no command items match the current search query.
289
- * Can contain custom content beyond simple text messages.
576
+ * Can contain custom content beyond simple text messages. Automatically
577
+ * shows/hides based on filtered results and announces changes to screen readers.
578
+ *
579
+ * Supports dynamic content using the `useCommandState` hook to access current
580
+ * search term and create contextual empty messages.
581
+ *
582
+ * @param className - Additional CSS classes
290
583
  *
291
- * @component
292
584
  * @example
293
585
  * ```tsx
294
586
  * <CommandList>
@@ -310,10 +602,26 @@ function CommandList({
310
602
  * </CommandEmpty>
311
603
  * ```
312
604
  *
605
+ * @example
606
+ * ```tsx
607
+ * // Dynamic empty message with search term
608
+ * function DynamicEmpty() {
609
+ * const search = useCommandState((state) => state.search);
610
+ * return (
611
+ * <CommandEmpty>
612
+ * {search ? `No results found for "${search}".` : 'Start typing to search...'}
613
+ * </CommandEmpty>
614
+ * );
615
+ * }
616
+ * ```
617
+ *
313
618
  * @accessibility
314
619
  * - Announced to screen readers when no results are found
315
- * - Content is properly focusable if interactive
620
+ * - Content is properly focusable if interactive elements are included
621
+ * - Live region updates when search state changes
622
+ * - Proper semantic markup for empty state content
316
623
  *
624
+ * @see {@link useCommandState} - Hook for accessing search state
317
625
  * @since 1.0.0
318
626
  */
319
627
  function CommandEmpty({
@@ -331,10 +639,18 @@ function CommandEmpty({
331
639
  /**
332
640
  * CommandGroup - Groups related command items under a heading
333
641
  *
334
- * Organizes command items into labeled sections for better organization
335
- * and navigation. Groups provide semantic structure for screen readers.
642
+ * Organizes command items into labeled sections with optional headings for better
643
+ * organization and navigation. Groups provide semantic structure for screen readers
644
+ * and can be hidden/shown based on whether they contain matching items.
645
+ *
646
+ * Groups automatically manage their visibility - if all contained items are filtered
647
+ * out, the entire group is hidden. Use `forceMount` to override this behavior.
648
+ *
649
+ * @param heading - The group heading text or component displayed above items
650
+ * @param value - Group identifier for programmatic control
651
+ * @param forceMount - Whether to always render the group even when hidden @default false
652
+ * @param className - Additional CSS classes
336
653
  *
337
- * @component
338
654
  * @example
339
655
  * ```tsx
340
656
  * <CommandGroup heading="Recent Files">
@@ -347,12 +663,35 @@ function CommandEmpty({
347
663
  * </CommandGroup>
348
664
  * ```
349
665
  *
350
- * @param heading - The group heading text displayed above items
666
+ * @example
667
+ * ```tsx
668
+ * // Group with custom heading component
669
+ * <CommandGroup
670
+ * heading={
671
+ * <div className="flex items-center gap-2">
672
+ * <FolderIcon className="h-4 w-4" />
673
+ * <span>Projects</span>
674
+ * </div>
675
+ * }
676
+ * >
677
+ * <CommandItem>Project A</CommandItem>
678
+ * <CommandItem>Project B</CommandItem>
679
+ * </CommandGroup>
680
+ * ```
681
+ *
682
+ * @example
683
+ * ```tsx
684
+ * // Group that always shows even when filtered
685
+ * <CommandGroup heading="Important" forceMount>
686
+ * <CommandItem>Critical Action</CommandItem>
687
+ * </CommandGroup>
688
+ * ```
351
689
  *
352
690
  * @accessibility
353
691
  * - Group headings are announced to screen readers
354
- * - Provides semantic grouping for better navigation
692
+ * - Provides semantic grouping with proper ARIA roles
355
693
  * - Maintains keyboard navigation flow between groups
694
+ * - Groups are properly labeled for assistive technology
356
695
  *
357
696
  * @since 1.0.0
358
697
  */
@@ -376,9 +715,15 @@ function CommandGroup({
376
715
  * CommandSeparator - Visual separator between command groups
377
716
  *
378
717
  * Provides visual separation between different sections of commands.
379
- * Purely decorative element that improves visual organization.
718
+ * Purely decorative element that improves visual organization and hierarchy.
719
+ * Automatically hides when adjacent groups are filtered out.
720
+ *
721
+ * The separator will only render when it has visible content before and after it,
722
+ * unless `alwaysRender` is true.
723
+ *
724
+ * @param alwaysRender - Whether to always show separator regardless of context @default false
725
+ * @param className - Additional CSS classes
380
726
  *
381
- * @component
382
727
  * @example
383
728
  * ```tsx
384
729
  * <CommandGroup heading="Recent">...</CommandGroup>
@@ -386,9 +731,18 @@ function CommandGroup({
386
731
  * <CommandGroup heading="Actions">...</CommandGroup>
387
732
  * ```
388
733
  *
734
+ * @example
735
+ * ```tsx
736
+ * // Separator that always renders
737
+ * <CommandGroup heading="Primary">...</CommandGroup>
738
+ * <CommandSeparator alwaysRender />
739
+ * <CommandGroup heading="Secondary">...</CommandGroup>
740
+ * ```
741
+ *
389
742
  * @accessibility
390
- * - Decorative element, properly hidden from screen readers
743
+ * - Decorative element, properly hidden from screen readers with aria-hidden
391
744
  * - Does not interfere with keyboard navigation
745
+ * - No focusable content or semantic meaning
392
746
  *
393
747
  * @since 1.0.0
394
748
  */
@@ -408,10 +762,20 @@ function CommandSeparator({
408
762
  /**
409
763
  * CommandItem - Individual selectable command item
410
764
  *
411
- * Represents a single command or action that can be selected via
412
- * keyboard or mouse interaction. Supports icons, text, and shortcuts.
765
+ * Represents a single command or action that can be selected via keyboard or mouse
766
+ * interaction. Supports icons, text, shortcuts, and advanced filtering with keywords.
767
+ * Items can be disabled, have custom values for filtering, and trigger callbacks on selection.
768
+ *
769
+ * The item's value is used for filtering - if not provided, it defaults to the text content.
770
+ * Keywords can be added to improve search matching without affecting the display.
771
+ *
772
+ * @param value - Unique identifier for filtering and selection (defaults to text content)
773
+ * @param keywords - Additional search terms for improved filtering
774
+ * @param disabled - Disables interaction and selection @default false
775
+ * @param onSelect - Callback fired when item is selected with Enter or click
776
+ * @param forceMount - Always render even when filtered out @default false
777
+ * @param className - Additional CSS classes
413
778
  *
414
- * @component
415
779
  * @example
416
780
  * ```tsx
417
781
  * // Basic command item
@@ -430,15 +794,33 @@ function CommandSeparator({
430
794
  * </CommandItem>
431
795
  * ```
432
796
  *
433
- * @param value - Unique identifier for the item (used for filtering)
434
- * @param onSelect - Callback when item is selected
435
- * @param disabled - Whether the item is disabled
797
+ * @example
798
+ * ```tsx
799
+ * // Item with keywords for better search
800
+ * <CommandItem
801
+ * value="apple"
802
+ * keywords={['fruit', 'red', 'healthy']}
803
+ * onSelect={() => selectFruit('apple')}
804
+ * >
805
+ * 🍎 Apple
806
+ * </CommandItem>
807
+ * ```
808
+ *
809
+ * @example
810
+ * ```tsx
811
+ * // Disabled item that shows but cannot be selected
812
+ * <CommandItem disabled>
813
+ * <LockIcon className="mr-2 h-4 w-4" />
814
+ * Premium Feature
815
+ * </CommandItem>
816
+ * ```
436
817
  *
437
818
  * @accessibility
438
819
  * - Keyboard selectable with Enter key and arrow navigation
439
- * - Visual highlight on focus and hover states
820
+ * - Visual highlight on focus and hover states with proper contrast
440
821
  * - Supports disabled state with proper ARIA attributes
441
- * - Screen reader announces item content and state
822
+ * - Screen reader announces item content, state, and shortcuts
823
+ * - Maintains focus within list boundaries
442
824
  *
443
825
  * @since 1.0.0
444
826
  */
@@ -461,10 +843,15 @@ function CommandItem({
461
843
  /**
462
844
  * CommandShortcut - Displays keyboard shortcut for command items
463
845
  *
464
- * Shows the keyboard shortcut associated with a command item,
465
- * typically displayed on the right side. Supports platform-specific shortcuts.
846
+ * Shows the keyboard shortcut associated with a command item, typically displayed
847
+ * on the right side with proper spacing. Supports platform-specific shortcuts and
848
+ * complex key combinations with consistent formatting.
849
+ *
850
+ * The component uses monospace-style formatting with appropriate opacity and spacing
851
+ * to clearly indicate keyboard shortcuts without overwhelming the item content.
852
+ *
853
+ * @param className - Additional CSS classes
466
854
  *
467
- * @component
468
855
  * @example
469
856
  * ```tsx
470
857
  * <CommandItem>
@@ -482,10 +869,29 @@ function CommandItem({
482
869
  * </CommandItem>
483
870
  * ```
484
871
  *
872
+ * @example
873
+ * ```tsx
874
+ * // Platform-specific shortcuts
875
+ * <CommandItem>
876
+ * Copy
877
+ * <CommandShortcut>{isMac ? '⌘C' : 'Ctrl+C'}</CommandShortcut>
878
+ * </CommandItem>
879
+ * ```
880
+ *
881
+ * @example
882
+ * ```tsx
883
+ * // Complex shortcuts with modifiers
884
+ * <CommandItem>
885
+ * Open Command Palette
886
+ * <CommandShortcut>⌘⇧P</CommandShortcut>
887
+ * </CommandItem>
888
+ * ```
889
+ *
485
890
  * @accessibility
486
891
  * - Semantically associated with its command item
487
892
  * - Announced to screen readers as part of item description
488
- * - Uses proper keyboard shortcut formatting
893
+ * - Uses proper keyboard shortcut formatting and labeling
894
+ * - Maintains readability with appropriate contrast
489
895
  *
490
896
  * @since 1.0.0
491
897
  */
@@ -505,6 +911,58 @@ function CommandShortcut({
505
911
  );
506
912
  }
507
913
 
914
+ /**
915
+ * CommandLoading - Loading state indicator for command menu
916
+ *
917
+ * Displays a loading message while command items are being fetched asynchronously.
918
+ * Should be conditionally rendered based on loading state. Shares the same styling
919
+ * and behavior as CommandEmpty but indicates active loading rather than empty state.
920
+ *
921
+ * @param className - Additional CSS classes
922
+ *
923
+ * @example
924
+ * ```tsx
925
+ * const [loading, setLoading] = useState(false);
926
+ * const [items, setItems] = useState([]);
927
+ *
928
+ * <CommandList>
929
+ * {loading && <CommandLoading>Fetching commands...</CommandLoading>}
930
+ * {items.map(item => (
931
+ * <CommandItem key={item.id} value={item.id}>
932
+ * {item.name}
933
+ * </CommandItem>
934
+ * ))}
935
+ * </CommandList>
936
+ * ```
937
+ *
938
+ * @example
939
+ * ```tsx
940
+ * // With custom loading indicator
941
+ * <CommandLoading>
942
+ * <div className="flex items-center justify-center py-6">
943
+ * <Spinner className="mr-2 h-4 w-4" />
944
+ * <span>Loading commands...</span>
945
+ * </div>
946
+ * </CommandLoading>
947
+ * ```
948
+ *
949
+ * @accessibility
950
+ * - Announced to screen readers as loading state
951
+ * - Uses appropriate ARIA live region for status updates
952
+ * - Provides context for users while waiting for results
953
+ *
954
+ * @since 1.0.0
955
+ */
956
+ function CommandLoading({ ...props }: React.ComponentProps<"div">) {
957
+ return (
958
+ <div
959
+ data-slot="command-loading"
960
+ className="py-6 text-center text-sm"
961
+ {...props}
962
+ />
963
+ );
964
+ }
965
+
508
966
  export {
509
967
  Command,
510
968
  CommandDialog,
@@ -515,4 +973,6 @@ export {
515
973
  CommandItem,
516
974
  CommandShortcut,
517
975
  CommandSeparator,
976
+ CommandLoading,
977
+ useCommandState,
518
978
  };