{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "command-menu", "type": "registry:ui", "title": "Command Menu", "description": "A macOS Spotlight-style command menu with animated search, keyboard navigation, and customizable groups. Features backdrop blur, smooth animations, and full keyboard support.", "dependencies": ["cmdk", "framer-motion", "lucide-react"], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/ui/command-menu.tsx", "type": "registry:ui", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ReactDOM from \"react-dom\"\nimport { Command } from \"cmdk\"\nimport { Search, ArrowRight, X } from \"lucide-react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface CommandMenuItem {\n id: string\n title: string\n group?: string\n icon?: React.ReactNode\n onSelect?: () => void\n}\n\nexport interface CommandMenuGroup {\n title: string\n items: CommandMenuItem[]\n}\n\nexport interface CommandMenuProps {\n groups: CommandMenuGroup[]\n placeholder?: string\n emptyMessage?: string\n brandName?: string\n triggerClassName?: string\n triggerLabel?: string\n shortcutKey?: string\n open?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nfunction CommandMenu({\n groups,\n placeholder = \"Search...\",\n emptyMessage = \"No results found\",\n brandName = \"Command Menu\",\n triggerClassName,\n triggerLabel = \"Search...\",\n shortcutKey = \"K\",\n open: controlledOpen,\n onOpenChange,\n}: CommandMenuProps) {\n const [internalOpen, setInternalOpen] = React.useState(false)\n const [query, setQuery] = React.useState(\"\")\n const inputRef = React.useRef(null)\n\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : internalOpen\n const setOpen = isControlled ? (onOpenChange ?? (() => {})) : setInternalOpen\n\n React.useEffect(() => {\n const down = (e: KeyboardEvent) => {\n if (e.key.toLowerCase() === shortcutKey.toLowerCase() && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n setOpen(!open)\n }\n if (e.key === \"Escape\") {\n setOpen(false)\n }\n }\n\n document.addEventListener(\"keydown\", down)\n return () => document.removeEventListener(\"keydown\", down)\n }, [open, setOpen, shortcutKey])\n\n React.useEffect(() => {\n if (open) {\n setTimeout(() => {\n inputRef.current?.focus()\n }, 0)\n } else {\n setQuery(\"\")\n }\n }, [open])\n\n const handleSelect = React.useCallback((item: CommandMenuItem) => {\n setOpen(false)\n item.onSelect?.()\n }, [setOpen])\n\n return (\n <>\n setOpen(true)}\n className={cn(\n \"group inline-flex items-center gap-2 whitespace-nowrap transition-all duration-200\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"border border-input/50 hover:border-input hover:bg-accent/50\",\n \"px-3 py-2 relative h-9 w-full justify-start rounded-lg bg-muted/30\",\n \"text-sm font-normal text-muted-foreground sm:pr-12 md:w-40 lg:w-56\",\n triggerClassName\n )}\n >\n \n {triggerLabel}\n Search\n \n \n\n {typeof document !== \"undefined\" && ReactDOM.createPortal(\n \n {open && (\n <>\n setOpen(false)}\n />\n \n \n
\n
\n \n
\n \n {query && (\n setQuery(\"\")}\n className=\"rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors\"\n >\n Clear\n \n )}\n \n ESC\n \n
\n\n \n \n
\n \n
\n

{emptyMessage}

\n

Try searching for something else

\n
\n\n {groups.map((group) => (\n \n {group.items.map((item) => (\n handleSelect(item)}\n className=\"group/item relative flex cursor-pointer select-none items-center gap-3 rounded-xl px-3 py-2.5 text-sm outline-none transition-colors hover:bg-accent/70 hover:text-accent-foreground aria-[selected='true']:bg-accent aria-[selected='true']:text-accent-foreground data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50\"\n >\n
\n {item.icon}\n
\n
\n {item.title}\n {group.title}\n
\n \n \n ))}\n
\n ))}\n
\n\n
\n
\n \n ↑↓\n Navigate\n \n \n \n Select\n \n
\n {brandName}\n
\n \n \n \n )}\n
,\n document.body\n )}\n \n )\n}\n\nCommandMenu.displayName = \"CommandMenu\"\n\nexport { CommandMenu }\n" } ], "tailwind": {}, "cssVars": {}, "meta": {} }