import * as React from "react"
import { SearchIcon, XIcon } from "lucide-react"
import { cn } from "../core/cn"
interface CommandItem {
value: string
label: string
description?: string
icon?: React.ReactNode
disabled?: boolean
group?: string
}
interface CommandProps {
items: CommandItem[]
value?: string
onValueChange?: (value: string) => void
placeholder?: string
emptyMessage?: string
className?: string
inputClassName?: string
listClassName?: string
}
function Command({ className, ...props }: React.ComponentPropsWithoutRef<"div">) {
return (
)
}
function CommandInput({ className, ...props }: React.ComponentPropsWithoutRef<"input">) {
return (
)
}
function CommandList({ className, ...props }: React.ComponentPropsWithoutRef<"div">) {
return (
)
}
function CommandEmpty({ className, ...props }: React.ComponentPropsWithoutRef<"div">) {
return (
)
}
function CommandGroup({ className, heading, ...props }: React.ComponentPropsWithoutRef<"div"> & { heading?: string }) {
return (
{heading && (
{heading}
)}
)
}
function CommandSeparator({ className, ...props }: React.ComponentPropsWithoutRef<"div">) {
return (
)
}
interface CommandItemProps extends React.ComponentPropsWithoutRef<"div"> {
selected?: boolean
disabled?: boolean
onSelect?: () => void
}
function CommandItem({ className, selected, disabled, onSelect, ...props }: CommandItemProps) {
return (
)
}
function CommandShortcut({ className, ...props }: React.ComponentPropsWithoutRef<"span">) {
return (
)
}
function CommandSearch({
items,
value,
onValueChange,
placeholder = "Search...",
emptyMessage = "No results found.",
className,
}: CommandProps) {
const [query, setQuery] = React.useState("")
const [selectedValue, setSelectedValue] = React.useState(value ?? "")
const filtered = React.useMemo(() => {
if (!query) return items
const q = query.toLowerCase()
return items.filter(
(item) =>
item.label.toLowerCase().includes(q) ||
item.description?.toLowerCase().includes(q) ||
item.value.toLowerCase().includes(q)
)
}, [items, query])
const groups = React.useMemo(() => {
const map = new Map()
for (const item of filtered) {
const key = item.group ?? ""
if (!map.has(key)) map.set(key, [])
map.get(key)!.push(item)
}
return map
}, [filtered])
const handleSelect = (val: string) => {
setSelectedValue(val)
onValueChange?.(val)
}
return (
setQuery(e.target.value)}
placeholder={placeholder}
/>
{filtered.length === 0 ? (
{emptyMessage}
) : (
Array.from(groups.entries()).map(([group, groupItems]) => (
{groupItems.map((item) => (
handleSelect(item.value)}
>
{item.icon && {item.icon}}
{item.label}
{item.description && (
{item.description}
)}
))}
))
)}
)
}
export { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSearch, CommandSeparator, CommandShortcut }
export type { CommandItem, CommandProps }