"use client" import * as React from "react" import { useRouter } from "next/navigation" import { type DialogProps } from "@radix-ui/react-dialog" import { IconArrowRight } from "@tabler/icons-react" import { CornerDownLeftIcon, SquareDashedIcon } from "lucide-react" import { type Color, type ColorPalette } from "@/lib/colors" import { showMcpDocs } from "@/lib/flags" import { source } from "@/lib/source" import { cn } from "@/lib/utils" import { useConfig } from "@/hooks/use-config" import { useIsMac } from "@/hooks/use-is-mac" import { useMutationObserver } from "@/hooks/use-mutation-observer" import { copyToClipboardWithMeta } from "@/components/copy-button" import { Button } from "@/registry/new-york-v4/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/registry/new-york-v4/ui/command" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/registry/new-york-v4/ui/dialog" import { Separator } from "@/registry/new-york-v4/ui/separator" export function CommandMenu({ tree, colors, blocks, navItems, ...props }: DialogProps & { tree: typeof source.pageTree colors: ColorPalette[] blocks?: { name: string; description: string; categories: string[] }[] navItems?: { href: string; label: string }[] }) { const router = useRouter() const isMac = useIsMac() const [config] = useConfig() const [open, setOpen] = React.useState(false) const [selectedType, setSelectedType] = React.useState< "color" | "page" | "component" | "block" | null >(null) const [copyPayload, setCopyPayload] = React.useState("") const packageManager = config.packageManager || "pnpm" const handlePageHighlight = React.useCallback( (isComponent: boolean, item: { url: string; name?: React.ReactNode }) => { if (isComponent) { const componentName = item.url.split("/").pop() setSelectedType("component") setCopyPayload( `${packageManager} dlx shadcn@latest add ${componentName}` ) } else { setSelectedType("page") setCopyPayload("") } }, [packageManager, setSelectedType, setCopyPayload] ) const handleColorHighlight = React.useCallback( (color: Color) => { setSelectedType("color") setCopyPayload(color.className) }, [setSelectedType, setCopyPayload] ) const handleBlockHighlight = React.useCallback( (block: { name: string; description: string; categories: string[] }) => { setSelectedType("block") setCopyPayload(`${packageManager} dlx shadcn@latest add ${block.name}`) }, [setSelectedType, setCopyPayload, packageManager] ) const runCommand = React.useCallback((command: () => unknown) => { setOpen(false) command() }, []) React.useEffect(() => { const down = (e: KeyboardEvent) => { if ((e.key === "k" && (e.metaKey || e.ctrlKey)) || e.key === "/") { if ( (e.target instanceof HTMLElement && e.target.isContentEditable) || e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLSelectElement ) { return } e.preventDefault() setOpen((open) => !open) } if (e.key === "c" && (e.metaKey || e.ctrlKey)) { runCommand(() => { if (selectedType === "color") { copyToClipboardWithMeta(copyPayload, { name: "copy_color", properties: { color: copyPayload }, }) } if (selectedType === "block") { copyToClipboardWithMeta(copyPayload, { name: "copy_npm_command", properties: { command: copyPayload, pm: packageManager }, }) } if (selectedType === "page" || selectedType === "component") { copyToClipboardWithMeta(copyPayload, { name: "copy_npm_command", properties: { command: copyPayload, pm: packageManager }, }) } }) } } document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) }, [copyPayload, runCommand, selectedType, packageManager]) return ( Search documentation... Search for a command to run... { const extendValue = value + " " + (keywords?.join(" ") || "") if (extendValue.toLowerCase().includes(search.toLowerCase())) { return 1 } return 0 }} > No results found. {navItems && navItems.length > 0 && ( {navItems.map((item) => ( { setSelectedType("page") setCopyPayload("") }} onSelect={() => { runCommand(() => router.push(item.href)) }} > {item.label} ))} )} {tree.children.map((group) => ( {group.type === "folder" && group.children.map((item) => { if (item.type === "page") { const isComponent = item.url.includes("/components/") if (!showMcpDocs && item.url.includes("/mcp")) { return null } return ( handlePageHighlight(isComponent, item) } onSelect={() => { runCommand(() => router.push(item.url)) }} > {isComponent ? (
) : ( )} {item.name} ) } return null })} ))} {colors.map((colorPalette) => ( {colorPalette.colors.map((color) => ( handleColorHighlight(color)} onSelect={() => { runCommand(() => copyToClipboardWithMeta(color.oklch, { name: "copy_color", properties: { color: color.oklch }, }) ) }} >
{color.className} {color.oklch} ))} ))} {blocks?.length ? ( {blocks.map((block) => ( { handleBlockHighlight(block) }} keywords={[ "block", block.name, block.description, ...block.categories, ]} onSelect={() => { runCommand(() => router.push( `/blocks/${block.categories[0]}#${block.name}` ) ) }} > {block.description} {block.name} ))} ) : null}
{" "} {selectedType === "page" || selectedType === "component" ? "Go to Page" : null} {selectedType === "color" ? "Copy OKLCH" : null}
{copyPayload && ( <>
{isMac ? "⌘" : "Ctrl"} C {copyPayload}
)}
) } function CommandMenuItem({ children, className, onHighlight, ...props }: React.ComponentProps & { onHighlight?: () => void "data-selected"?: string "aria-selected"?: string }) { const ref = React.useRef(null) useMutationObserver(ref, (mutations) => { mutations.forEach((mutation) => { if ( mutation.type === "attributes" && mutation.attributeName === "aria-selected" && ref.current?.getAttribute("aria-selected") === "true" ) { onHighlight?.() } }) }) return ( {children} ) } function CommandMenuKbd({ className, ...props }: React.ComponentProps<"kbd">) { return ( ) }