command.tsx raw
1 import * as React from "react";
2 import { Command as CommandPrimitive } from "cmdk";
3 import { SearchIcon } from "lucide-react";
4
5 import { cn } from "src/lib/utils";
6 import {
7 Dialog,
8 DialogContent,
9 DialogDescription,
10 DialogHeader,
11 DialogTitle,
12 } from "src/components/ui/dialog";
13
14 function Command({
15 className,
16 ...props
17 }: React.ComponentProps<typeof CommandPrimitive>) {
18 return (
19 <CommandPrimitive
20 data-slot="command"
21 className={cn(
22 "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
23 className
24 )}
25 {...props}
26 />
27 );
28 }
29
30 function CommandDialog({
31 title = "Command Palette",
32 description = "Search for a command to run...",
33 children,
34 className,
35 showCloseButton = true,
36 ...props
37 }: React.ComponentProps<typeof Dialog> & {
38 title?: string;
39 description?: string;
40 className?: string;
41 showCloseButton?: boolean;
42 }) {
43 return (
44 <Dialog {...props}>
45 <DialogHeader className="sr-only">
46 <DialogTitle>{title}</DialogTitle>
47 <DialogDescription>{description}</DialogDescription>
48 </DialogHeader>
49 <DialogContent
50 className={cn("overflow-hidden p-0", className)}
51 showCloseButton={showCloseButton}
52 >
53 <Command className="**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
54 {children}
55 </Command>
56 </DialogContent>
57 </Dialog>
58 );
59 }
60
61 function CommandInput({
62 className,
63 ...props
64 }: React.ComponentProps<typeof CommandPrimitive.Input>) {
65 return (
66 <div
67 data-slot="command-input-wrapper"
68 className="flex h-9 items-center gap-2 border-b px-3"
69 >
70 <SearchIcon className="size-4 shrink-0 opacity-50" />
71 <CommandPrimitive.Input
72 data-slot="command-input"
73 className={cn(
74 "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
75 className
76 )}
77 {...props}
78 />
79 </div>
80 );
81 }
82
83 function CommandList({
84 className,
85 ...props
86 }: React.ComponentProps<typeof CommandPrimitive.List>) {
87 return (
88 <CommandPrimitive.List
89 data-slot="command-list"
90 className={cn(
91 "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
92 className
93 )}
94 {...props}
95 />
96 );
97 }
98
99 function CommandEmpty({
100 ...props
101 }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
102 return (
103 <CommandPrimitive.Empty
104 data-slot="command-empty"
105 className="py-6 text-center text-sm"
106 {...props}
107 />
108 );
109 }
110
111 function CommandGroup({
112 className,
113 ...props
114 }: React.ComponentProps<typeof CommandPrimitive.Group>) {
115 return (
116 <CommandPrimitive.Group
117 data-slot="command-group"
118 className={cn(
119 "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
120 className
121 )}
122 {...props}
123 />
124 );
125 }
126
127 function CommandSeparator({
128 className,
129 ...props
130 }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
131 return (
132 <CommandPrimitive.Separator
133 data-slot="command-separator"
134 className={cn("-mx-1 h-px bg-border", className)}
135 {...props}
136 />
137 );
138 }
139
140 function CommandItem({
141 className,
142 ...props
143 }: React.ComponentProps<typeof CommandPrimitive.Item>) {
144 return (
145 <CommandPrimitive.Item
146 data-slot="command-item"
147 className={cn(
148 "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
149 className
150 )}
151 {...props}
152 />
153 );
154 }
155
156 function CommandShortcut({
157 className,
158 ...props
159 }: React.ComponentProps<"span">) {
160 return (
161 <span
162 data-slot="command-shortcut"
163 className={cn(
164 "ml-auto text-xs tracking-widest text-muted-foreground",
165 className
166 )}
167 {...props}
168 />
169 );
170 }
171
172 export {
173 Command,
174 CommandDialog,
175 CommandInput,
176 CommandList,
177 CommandEmpty,
178 CommandGroup,
179 CommandItem,
180 CommandShortcut,
181 CommandSeparator,
182 };
183