-
Notifications
You must be signed in to change notification settings - Fork 164
/
Copy pathcommand-menu.tsx
125 lines (117 loc) · 4.33 KB
/
command-menu.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"use client";
import * as React from "react";
import { useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
import { docsConfig } from "@/config/docs";
import { cn } from "@/lib/utils";
import { DialogProps } from "@radix-ui/react-alert-dialog";
import { CircleIcon, FileIcon, LaptopIcon, MoonIcon, SunIcon } from "@radix-ui/react-icons";
export function CommandMenu({ ...props }: DialogProps) {
const router = useRouter();
const [open, setOpen] = React.useState(false);
const { setTheme } = useTheme();
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);
}
};
document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down);
}, []);
const runCommand = React.useCallback((command: () => unknown) => {
setOpen(false);
command();
}, []);
return (
<>
<Button
variant="outline"
className={cn(
"relative h-10 w-full justify-start gap-2 rounded-xl border-none bg-zinc-600 px-4 text-sm font-normal text-background/75 shadow-none transition-all duration-300 hover:bg-zinc-600/75 hover:text-background dark:bg-slate-200/75 dark:hover:bg-slate-200",
)}
onClick={() => setOpen(true)}
{...props}
>
<span className="inline-flex flex-1">Search...</span>
<kbd className="pointer-events-none hidden flex-shrink-0 select-none items-center gap-1 rounded border border-muted-foreground bg-zinc-700 px-2 font-mono text-[10px] font-medium dark:bg-slate-200 sm:flex">
<span className="text-xs">⌘</span>K
</kbd>
</Button>
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Links">
{docsConfig.mainNav
.filter((item) => !item.external)
.map((navItem) => (
<CommandItem
key={navItem.href}
value={navItem.title}
onSelect={() => {
runCommand(() => router.push(navItem.href as string));
}}
>
<FileIcon className="mr-2 h-4 w-4" />
{navItem.title}
</CommandItem>
))}
</CommandGroup>
{docsConfig.sidebarNav.map((group) => (
<CommandGroup key={group.title} heading={group.title}>
{group.items?.map((navItem) => (
<CommandItem
key={navItem.href}
value={navItem.title}
onSelect={() => {
runCommand(() => router.push(navItem.href as string));
}}
>
<div className="mr-2 flex h-4 w-4 items-center justify-center">
<CircleIcon className="h-3 w-3" />
</div>
{navItem.title}
</CommandItem>
))}
</CommandGroup>
))}
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}>
<SunIcon className="mr-2 h-4 w-4" />
Light
</CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("dark"))}>
<MoonIcon className="mr-2 h-4 w-4" />
Dark
</CommandItem>
<CommandItem onSelect={() => runCommand(() => setTheme("system"))}>
<LaptopIcon className="mr-2 h-4 w-4" />
System
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
</>
);
}