@srcroot/ui 0.0.54 → 0.0.56

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 (107) hide show
  1. package/README.md +151 -151
  2. package/dist/index.d.ts +0 -0
  3. package/dist/index.js +55 -1
  4. package/package.json +7 -2
  5. package/src/registry/analytics/google-analytics.tsx +36 -39
  6. package/src/registry/analytics/google-tag-manager.tsx +62 -65
  7. package/src/registry/analytics/meta-pixel.tsx +44 -47
  8. package/src/registry/analytics/microsoft-clarity.tsx +31 -34
  9. package/src/registry/analytics/tiktok-pixel.tsx +34 -37
  10. package/src/registry/lib/utils.ts +0 -0
  11. package/src/registry/themes/v3/blue.css +157 -157
  12. package/src/registry/themes/v3/glass.css +153 -153
  13. package/src/registry/themes/v3/gray.css +157 -157
  14. package/src/registry/themes/v3/green.css +157 -157
  15. package/src/registry/themes/v3/neutral.css +157 -157
  16. package/src/registry/themes/v3/orange.css +157 -157
  17. package/src/registry/themes/v3/rose.css +157 -157
  18. package/src/registry/themes/v3/slate.css +157 -157
  19. package/src/registry/themes/v3/stone.css +157 -157
  20. package/src/registry/themes/v3/violet.css +186 -186
  21. package/src/registry/themes/v3/zinc.css +157 -157
  22. package/src/registry/themes/v4/blue.css +184 -184
  23. package/src/registry/themes/v4/glass.css +180 -180
  24. package/src/registry/themes/v4/gray.css +184 -184
  25. package/src/registry/themes/v4/green.css +184 -184
  26. package/src/registry/themes/v4/neutral.css +184 -184
  27. package/src/registry/themes/v4/orange.css +184 -184
  28. package/src/registry/themes/v4/rose.css +184 -184
  29. package/src/registry/themes/v4/slate.css +184 -184
  30. package/src/registry/themes/v4/stone.css +184 -184
  31. package/src/registry/themes/v4/violet.css +184 -184
  32. package/src/registry/themes/v4/zinc.css +184 -184
  33. package/src/registry/ui/accordion.tsx +164 -165
  34. package/src/registry/ui/alert-dialog.tsx +213 -214
  35. package/src/registry/ui/alert.tsx +73 -76
  36. package/src/registry/ui/aspect-ratio.tsx +44 -47
  37. package/src/registry/ui/avatar.tsx +96 -97
  38. package/src/registry/ui/badge.tsx +52 -55
  39. package/src/registry/ui/breadcrumb.tsx +147 -150
  40. package/src/registry/ui/button-group.tsx +64 -67
  41. package/src/registry/ui/button.tsx +71 -72
  42. package/src/registry/ui/calendar.tsx +514 -515
  43. package/src/registry/ui/card.tsx +88 -91
  44. package/src/registry/ui/carousel.tsx +214 -214
  45. package/src/registry/ui/chart.tsx +373 -373
  46. package/src/registry/ui/chatbot.tsx +86 -13
  47. package/src/registry/ui/checkbox.tsx +93 -94
  48. package/src/registry/ui/collapsible.tsx +107 -108
  49. package/src/registry/ui/combobox.tsx +171 -171
  50. package/src/registry/ui/command.tsx +300 -300
  51. package/src/registry/ui/container.tsx +44 -47
  52. package/src/registry/ui/context-menu.tsx +221 -221
  53. package/src/registry/ui/date-picker.tsx +228 -228
  54. package/src/registry/ui/dialog.tsx +269 -270
  55. package/src/registry/ui/drawer.tsx +10 -4
  56. package/src/registry/ui/dropdown-menu.tsx +529 -530
  57. package/src/registry/ui/empty-state.tsx +0 -2
  58. package/src/registry/ui/file-upload.tsx +0 -0
  59. package/src/registry/ui/floating-dock.tsx +0 -0
  60. package/src/registry/ui/form-field.tsx +91 -94
  61. package/src/registry/ui/google-analytics.tsx +38 -0
  62. package/src/registry/ui/google-tag-manager.tsx +64 -0
  63. package/src/registry/ui/hover-card.tsx +223 -223
  64. package/src/registry/ui/image.tsx +144 -147
  65. package/src/registry/ui/input-group.tsx +82 -85
  66. package/src/registry/ui/input.tsx +125 -125
  67. package/src/registry/ui/kbd.tsx +60 -63
  68. package/src/registry/ui/label.tsx +36 -37
  69. package/src/registry/ui/loading-spinner.tsx +108 -111
  70. package/src/registry/ui/map.tsx +0 -0
  71. package/src/registry/ui/marquee.tsx +2 -0
  72. package/src/registry/ui/menubar.tsx +246 -246
  73. package/src/registry/ui/meta-pixel.tsx +46 -0
  74. package/src/registry/ui/microsoft-clarity.tsx +33 -0
  75. package/src/registry/ui/native-select.tsx +49 -52
  76. package/src/registry/ui/otp-input.tsx +152 -155
  77. package/src/registry/ui/pagination.tsx +149 -152
  78. package/src/registry/ui/patterns.tsx +28 -0
  79. package/src/registry/ui/popover.tsx +226 -227
  80. package/src/registry/ui/progress.tsx +51 -52
  81. package/src/registry/ui/radio.tsx +99 -102
  82. package/src/registry/ui/resizable.tsx +314 -314
  83. package/src/registry/ui/scroll-animation.tsx +45 -0
  84. package/src/registry/ui/scroll-area.tsx +121 -122
  85. package/src/registry/ui/scroll-to-top.tsx +0 -0
  86. package/src/registry/ui/search.tsx +147 -150
  87. package/src/registry/ui/select.tsx +292 -293
  88. package/src/registry/ui/separator.tsx +46 -47
  89. package/src/registry/ui/sheet.tsx +6 -3
  90. package/src/registry/ui/sidebar.tsx +628 -628
  91. package/src/registry/ui/skeleton.tsx +26 -29
  92. package/src/registry/ui/slider.tsx +196 -197
  93. package/src/registry/ui/slot.tsx +69 -72
  94. package/src/registry/ui/star-rating.tsx +131 -134
  95. package/src/registry/ui/switch.tsx +72 -73
  96. package/src/registry/ui/table-of-contents.tsx +96 -96
  97. package/src/registry/ui/table.tsx +138 -139
  98. package/src/registry/ui/tabs.tsx +124 -125
  99. package/src/registry/ui/text.tsx +61 -64
  100. package/src/registry/ui/textarea.tsx +41 -42
  101. package/src/registry/ui/theme-switcher.tsx +66 -66
  102. package/src/registry/ui/tiktok-pixel.tsx +36 -0
  103. package/src/registry/ui/toast.tsx +97 -98
  104. package/src/registry/ui/toggle-group.tsx +129 -129
  105. package/src/registry/ui/toggle.tsx +72 -72
  106. package/src/registry/ui/tooltip.tsx +143 -144
  107. package/src/registry/ui/whatsapp.tsx +0 -0
@@ -1,300 +1,300 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { LuSearch } from "react-icons/lu"
5
- import { cn } from "@/lib/utils"
6
- import { Dialog, DialogContent } from "@/components/ui/dialog"
7
-
8
- interface CommandContextValue {
9
- search: string
10
- setSearch: (search: string) => void
11
- selectedIndex: number
12
- setSelectedIndex: (index: number) => void
13
- items: string[]
14
- registerItem: (value: string) => void
15
- unregisterItem: (value: string) => void
16
- }
17
-
18
- const CommandContext = React.createContext<CommandContextValue | null>(null)
19
-
20
- function useCommandContext() {
21
- const context = React.useContext(CommandContext)
22
- if (!context) {
23
- throw new Error("Command components must be used within Command")
24
- }
25
- return context
26
- }
27
-
28
- interface CommandProps extends React.HTMLAttributes<HTMLDivElement> {
29
- children: React.ReactNode
30
- }
31
-
32
- const Command = React.forwardRef<HTMLDivElement, CommandProps>(
33
- ({ className, children, ...props }, ref) => {
34
- const [search, setSearch] = React.useState("")
35
- const [selectedIndex, setSelectedIndex] = React.useState(0)
36
- const [items, setItems] = React.useState<string[]>([])
37
-
38
- const registerItem = React.useCallback((value: string) => {
39
- setItems((prev) => [...prev, value])
40
- }, [])
41
-
42
- const unregisterItem = React.useCallback((value: string) => {
43
- setItems((prev) => prev.filter((item) => item !== value))
44
- }, [])
45
-
46
- // Handle keyboard navigation
47
- const handleKeyDown = (e: React.KeyboardEvent) => {
48
- switch (e.key) {
49
- case "ArrowDown":
50
- e.preventDefault()
51
- setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1))
52
- break
53
- case "ArrowUp":
54
- e.preventDefault()
55
- setSelectedIndex((prev) => Math.max(prev - 1, 0))
56
- break
57
- case "Enter":
58
- e.preventDefault()
59
- const selectedItem = document.querySelector('[data-selected="true"]') as HTMLElement
60
- selectedItem?.click()
61
- break
62
- }
63
- }
64
-
65
- return (
66
- <CommandContext.Provider
67
- value={{
68
- search,
69
- setSearch,
70
- selectedIndex,
71
- setSelectedIndex,
72
- items,
73
- registerItem,
74
- unregisterItem,
75
- }}
76
- >
77
- <div
78
- ref={ref}
79
- className={cn(
80
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
81
- className
82
- )}
83
- onKeyDown={handleKeyDown}
84
- {...props}
85
- >
86
- {children}
87
- </div>
88
- </CommandContext.Provider>
89
- )
90
- }
91
- )
92
- Command.displayName = "Command"
93
-
94
- interface CommandDialogProps {
95
- children: React.ReactNode
96
- open?: boolean
97
- onOpenChange?: (open: boolean) => void
98
- }
99
-
100
- const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
101
- return (
102
- <Dialog {...props}>
103
- <DialogContent className="overflow-hidden p-0 shadow-lg">
104
- <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground">
105
- {children}
106
- </Command>
107
- </DialogContent>
108
- </Dialog>
109
- )
110
- }
111
-
112
- interface CommandInputProps extends React.InputHTMLAttributes<HTMLInputElement> { }
113
-
114
- const CommandInput = React.forwardRef<HTMLInputElement, CommandInputProps>(
115
- ({ className, ...props }, ref) => {
116
- const { search, setSearch, setSelectedIndex } = useCommandContext()
117
-
118
- return (
119
- <div className="flex items-center border-b px-3">
120
- <LuSearch className="mr-2 h-4 w-4 shrink-0 opacity-50" />
121
- <input
122
- ref={ref}
123
- value={search}
124
- onChange={(e) => {
125
- setSearch(e.target.value)
126
- setSelectedIndex(0)
127
- }}
128
- className={cn(
129
- "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
130
- className
131
- )}
132
- {...props}
133
- />
134
- </div>
135
- )
136
- }
137
- )
138
- CommandInput.displayName = "CommandInput"
139
-
140
- interface CommandListProps extends React.HTMLAttributes<HTMLDivElement> { }
141
-
142
- const CommandList = React.forwardRef<HTMLDivElement, CommandListProps>(
143
- ({ className, ...props }, ref) => (
144
- <div
145
- ref={ref}
146
- className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
147
- {...props}
148
- />
149
- )
150
- )
151
- CommandList.displayName = "CommandList"
152
-
153
- interface CommandEmptyProps extends React.HTMLAttributes<HTMLDivElement> { }
154
-
155
- const CommandEmpty = React.forwardRef<HTMLDivElement, CommandEmptyProps>(
156
- ({ className, children, ...props }, ref) => {
157
- const { search, items } = useCommandContext()
158
-
159
- // Count how many items would be visible with the current search
160
- const visibleCount = search
161
- ? items.filter(item => item.toLowerCase().includes(search.toLowerCase())).length
162
- : items.length
163
-
164
- // Only show empty message when user has typed something but no results match
165
- if (!search || visibleCount > 0) {
166
- return null
167
- }
168
-
169
- return (
170
- <div
171
- ref={ref}
172
- className={cn("py-6 text-center text-sm", className)}
173
- {...props}
174
- >
175
- {children}
176
- </div>
177
- )
178
- }
179
- )
180
- CommandEmpty.displayName = "CommandEmpty"
181
-
182
- interface CommandGroupProps extends React.HTMLAttributes<HTMLDivElement> {
183
- heading?: string
184
- }
185
-
186
- const CommandGroup = React.forwardRef<HTMLDivElement, CommandGroupProps>(
187
- ({ className, heading, children, ...props }, ref) => (
188
- <div
189
- ref={ref}
190
- className={cn(
191
- "overflow-hidden p-1 text-foreground",
192
- className
193
- )}
194
- {...props}
195
- >
196
- {heading && (
197
- <div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">
198
- {heading}
199
- </div>
200
- )}
201
- {children}
202
- </div>
203
- )
204
- )
205
- CommandGroup.displayName = "CommandGroup"
206
-
207
- interface CommandSeparatorProps extends React.HTMLAttributes<HTMLDivElement> { }
208
-
209
- const CommandSeparator = React.forwardRef<HTMLDivElement, CommandSeparatorProps>(
210
- ({ className, ...props }, ref) => (
211
- <div
212
- ref={ref}
213
- className={cn("-mx-1 h-px bg-border", className)}
214
- {...props}
215
- />
216
- )
217
- )
218
- CommandSeparator.displayName = "CommandSeparator"
219
-
220
- interface CommandItemProps extends React.HTMLAttributes<HTMLDivElement> {
221
- value?: string
222
- onSelect?: () => void
223
- disabled?: boolean
224
- }
225
-
226
- const CommandItem = React.forwardRef<HTMLDivElement, CommandItemProps>(
227
- ({ className, value, onSelect, disabled, children, ...props }, ref) => {
228
- const { search, selectedIndex, setSelectedIndex, registerItem, unregisterItem, items } = useCommandContext()
229
- const itemValue = value || (typeof children === "string" ? children : "")
230
-
231
- // Register/unregister on mount
232
- React.useEffect(() => {
233
- registerItem(itemValue)
234
- return () => unregisterItem(itemValue)
235
- }, [itemValue, registerItem, unregisterItem])
236
-
237
- // Filter based on search
238
- const isVisible = !search || itemValue.toLowerCase().includes(search.toLowerCase())
239
-
240
- if (!isVisible) return null
241
-
242
- const itemIndex = items.indexOf(itemValue)
243
- const isSelected = itemIndex === selectedIndex
244
-
245
- return (
246
- <div
247
- ref={ref}
248
- role="option"
249
- aria-selected={isSelected}
250
- data-selected={isSelected}
251
- data-disabled={disabled}
252
- className={cn(
253
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
254
- isSelected && "bg-accent text-accent-foreground",
255
- disabled && "pointer-events-none opacity-50",
256
- !disabled && "cursor-pointer",
257
- className
258
- )}
259
- onClick={() => {
260
- if (!disabled) {
261
- onSelect?.()
262
- }
263
- }}
264
- onMouseEnter={() => setSelectedIndex(itemIndex)}
265
- {...props}
266
- >
267
- {children}
268
- </div>
269
- )
270
- }
271
- )
272
- CommandItem.displayName = "CommandItem"
273
-
274
- const CommandShortcut = ({
275
- className,
276
- ...props
277
- }: React.HTMLAttributes<HTMLSpanElement>) => {
278
- return (
279
- <span
280
- className={cn(
281
- "ml-auto text-xs tracking-widest text-muted-foreground",
282
- className
283
- )}
284
- {...props}
285
- />
286
- )
287
- }
288
- CommandShortcut.displayName = "CommandShortcut"
289
-
290
- export {
291
- Command,
292
- CommandDialog,
293
- CommandInput,
294
- CommandList,
295
- CommandEmpty,
296
- CommandGroup,
297
- CommandItem,
298
- CommandShortcut,
299
- CommandSeparator,
300
- }
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { LuSearch } from "react-icons/lu"
5
+ import { cn } from "@/lib/utils"
6
+ import { Dialog, DialogContent } from "@/components/ui/dialog"
7
+
8
+ interface CommandContextValue {
9
+ search: string
10
+ setSearch: (search: string) => void
11
+ selectedIndex: number
12
+ setSelectedIndex: (index: number) => void
13
+ items: string[]
14
+ registerItem: (value: string) => void
15
+ unregisterItem: (value: string) => void
16
+ }
17
+
18
+ const CommandContext = React.createContext<CommandContextValue | null>(null)
19
+
20
+ function useCommandContext() {
21
+ const context = React.useContext(CommandContext)
22
+ if (!context) {
23
+ throw new Error("Command components must be used within Command")
24
+ }
25
+ return context
26
+ }
27
+
28
+ interface CommandProps extends React.HTMLAttributes<HTMLDivElement> {
29
+ children: React.ReactNode
30
+ }
31
+
32
+ const Command = React.forwardRef<HTMLDivElement, CommandProps>(
33
+ ({ className, children, ...props }, ref) => {
34
+ const [search, setSearch] = React.useState("")
35
+ const [selectedIndex, setSelectedIndex] = React.useState(0)
36
+ const [items, setItems] = React.useState<string[]>([])
37
+
38
+ const registerItem = React.useCallback((value: string) => {
39
+ setItems((prev) => [...prev, value])
40
+ }, [])
41
+
42
+ const unregisterItem = React.useCallback((value: string) => {
43
+ setItems((prev) => prev.filter((item) => item !== value))
44
+ }, [])
45
+
46
+ // Handle keyboard navigation
47
+ const handleKeyDown = (e: React.KeyboardEvent) => {
48
+ switch (e.key) {
49
+ case "ArrowDown":
50
+ e.preventDefault()
51
+ setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1))
52
+ break
53
+ case "ArrowUp":
54
+ e.preventDefault()
55
+ setSelectedIndex((prev) => Math.max(prev - 1, 0))
56
+ break
57
+ case "Enter":
58
+ e.preventDefault()
59
+ const selectedItem = document.querySelector('[data-selected="true"]') as HTMLElement
60
+ selectedItem?.click()
61
+ break
62
+ }
63
+ }
64
+
65
+ return (
66
+ <CommandContext.Provider
67
+ value={{
68
+ search,
69
+ setSearch,
70
+ selectedIndex,
71
+ setSelectedIndex,
72
+ items,
73
+ registerItem,
74
+ unregisterItem,
75
+ }}
76
+ >
77
+ <div
78
+ ref={ref}
79
+ className={cn(
80
+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
81
+ className
82
+ )}
83
+ onKeyDown={handleKeyDown}
84
+ {...props}
85
+ >
86
+ {children}
87
+ </div>
88
+ </CommandContext.Provider>
89
+ )
90
+ }
91
+ )
92
+ Command.displayName = "Command"
93
+
94
+ interface CommandDialogProps {
95
+ children: React.ReactNode
96
+ open?: boolean
97
+ onOpenChange?: (open: boolean) => void
98
+ }
99
+
100
+ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
101
+ return (
102
+ <Dialog {...props}>
103
+ <DialogContent className="overflow-hidden p-0 shadow-lg">
104
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground">
105
+ {children}
106
+ </Command>
107
+ </DialogContent>
108
+ </Dialog>
109
+ )
110
+ }
111
+
112
+ interface CommandInputProps extends React.InputHTMLAttributes<HTMLInputElement> { }
113
+
114
+ const CommandInput = React.forwardRef<HTMLInputElement, CommandInputProps>(
115
+ ({ className, ...props }, ref) => {
116
+ const { search, setSearch, setSelectedIndex } = useCommandContext()
117
+
118
+ return (
119
+ <div className="flex items-center border-b px-3">
120
+ <LuSearch className="mr-2 h-4 w-4 shrink-0 opacity-50" />
121
+ <input
122
+ ref={ref}
123
+ value={search}
124
+ onChange={(e) => {
125
+ setSearch(e.target.value)
126
+ setSelectedIndex(0)
127
+ }}
128
+ className={cn(
129
+ "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
130
+ className
131
+ )}
132
+ {...props}
133
+ />
134
+ </div>
135
+ )
136
+ }
137
+ )
138
+ CommandInput.displayName = "CommandInput"
139
+
140
+ interface CommandListProps extends React.HTMLAttributes<HTMLDivElement> { }
141
+
142
+ const CommandList = React.forwardRef<HTMLDivElement, CommandListProps>(
143
+ ({ className, ...props }, ref) => (
144
+ <div
145
+ ref={ref}
146
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
147
+ {...props}
148
+ />
149
+ )
150
+ )
151
+ CommandList.displayName = "CommandList"
152
+
153
+ interface CommandEmptyProps extends React.HTMLAttributes<HTMLDivElement> { }
154
+
155
+ const CommandEmpty = React.forwardRef<HTMLDivElement, CommandEmptyProps>(
156
+ ({ className, children, ...props }, ref) => {
157
+ const { search, items } = useCommandContext()
158
+
159
+ // Count how many items would be visible with the current search
160
+ const visibleCount = search
161
+ ? items.filter(item => item.toLowerCase().includes(search.toLowerCase())).length
162
+ : items.length
163
+
164
+ // Only show empty message when user has typed something but no results match
165
+ if (!search || visibleCount > 0) {
166
+ return null
167
+ }
168
+
169
+ return (
170
+ <div
171
+ ref={ref}
172
+ className={cn("py-6 text-center text-sm", className)}
173
+ {...props}
174
+ >
175
+ {children}
176
+ </div>
177
+ )
178
+ }
179
+ )
180
+ CommandEmpty.displayName = "CommandEmpty"
181
+
182
+ interface CommandGroupProps extends React.HTMLAttributes<HTMLDivElement> {
183
+ heading?: string
184
+ }
185
+
186
+ const CommandGroup = React.forwardRef<HTMLDivElement, CommandGroupProps>(
187
+ ({ className, heading, children, ...props }, ref) => (
188
+ <div
189
+ ref={ref}
190
+ className={cn(
191
+ "overflow-hidden p-1 text-foreground",
192
+ className
193
+ )}
194
+ {...props}
195
+ >
196
+ {heading && (
197
+ <div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">
198
+ {heading}
199
+ </div>
200
+ )}
201
+ {children}
202
+ </div>
203
+ )
204
+ )
205
+ CommandGroup.displayName = "CommandGroup"
206
+
207
+ interface CommandSeparatorProps extends React.HTMLAttributes<HTMLDivElement> { }
208
+
209
+ const CommandSeparator = React.forwardRef<HTMLDivElement, CommandSeparatorProps>(
210
+ ({ className, ...props }, ref) => (
211
+ <div
212
+ ref={ref}
213
+ className={cn("-mx-1 h-px bg-border", className)}
214
+ {...props}
215
+ />
216
+ )
217
+ )
218
+ CommandSeparator.displayName = "CommandSeparator"
219
+
220
+ interface CommandItemProps extends React.HTMLAttributes<HTMLDivElement> {
221
+ value?: string
222
+ onSelect?: () => void
223
+ disabled?: boolean
224
+ }
225
+
226
+ const CommandItem = React.forwardRef<HTMLDivElement, CommandItemProps>(
227
+ ({ className, value, onSelect, disabled, children, ...props }, ref) => {
228
+ const { search, selectedIndex, setSelectedIndex, registerItem, unregisterItem, items } = useCommandContext()
229
+ const itemValue = value || (typeof children === "string" ? children : "")
230
+
231
+ // Register/unregister on mount
232
+ React.useEffect(() => {
233
+ registerItem(itemValue)
234
+ return () => unregisterItem(itemValue)
235
+ }, [itemValue, registerItem, unregisterItem])
236
+
237
+ // Filter based on search
238
+ const isVisible = !search || itemValue.toLowerCase().includes(search.toLowerCase())
239
+
240
+ if (!isVisible) return null
241
+
242
+ const itemIndex = items.indexOf(itemValue)
243
+ const isSelected = itemIndex === selectedIndex
244
+
245
+ return (
246
+ <div
247
+ ref={ref}
248
+ role="option"
249
+ aria-selected={isSelected}
250
+ data-selected={isSelected}
251
+ data-disabled={disabled}
252
+ className={cn(
253
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
254
+ isSelected && "bg-accent text-accent-foreground",
255
+ disabled && "pointer-events-none opacity-50",
256
+ !disabled && "cursor-pointer",
257
+ className
258
+ )}
259
+ onClick={() => {
260
+ if (!disabled) {
261
+ onSelect?.()
262
+ }
263
+ }}
264
+ onMouseEnter={() => setSelectedIndex(itemIndex)}
265
+ {...props}
266
+ >
267
+ {children}
268
+ </div>
269
+ )
270
+ }
271
+ )
272
+ CommandItem.displayName = "CommandItem"
273
+
274
+ const CommandShortcut = ({
275
+ className,
276
+ ...props
277
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
278
+ return (
279
+ <span
280
+ className={cn(
281
+ "ml-auto text-xs tracking-widest text-muted-foreground",
282
+ className
283
+ )}
284
+ {...props}
285
+ />
286
+ )
287
+ }
288
+ CommandShortcut.displayName = "CommandShortcut"
289
+
290
+ export {
291
+ Command,
292
+ CommandDialog,
293
+ CommandInput,
294
+ CommandList,
295
+ CommandEmpty,
296
+ CommandGroup,
297
+ CommandItem,
298
+ CommandShortcut,
299
+ CommandSeparator,
300
+ }