"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 { 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 "@/components/ui/button" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Separator } from "@/components/ui/separator" export function CommandMenu({ tree, blocks, navItems, ...props }: DialogProps & { tree: typeof source.pageTree 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< "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 @creative-tim/ui@latest add ${componentName}` ) } else { setSelectedType("page") setCopyPayload("") } }, [packageManager, setSelectedType, setCopyPayload] ) const handleBlockHighlight = React.useCallback( (block: { name: string; description: string; categories: string[] }) => { setSelectedType("block") setCopyPayload( `${packageManager} dlx @creative-tim/ui@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 === "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 ( setOpen(true)} {...props} > Search doc... Search... {isMac ? "⌘" : "Ctrl"} K 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/") return ( handlePageHighlight(isComponent, item) } onSelect={() => { runCommand(() => router.push(item.url)) }} > {isComponent ? ( ) : ( )} {item.name} ) } return null })} ))} {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} {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 ( ) }