"use client";
import { useState, useEffect } from "react";
import {
Search,
FileText,
Settings,
Users,
PlusCircle,
Download,
Upload,
Trash2,
Share2,
Copy,
LogOut,
Moon,
Sun,
Bell,
HelpCircle,
Command,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Command as CommandPrimitive,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";
const commandGroups = [
{
heading: "Recent",
commands: [
{ icon: FileText, label: "Open Dashboard", shortcut: "⌘D" },
{ icon: Settings, label: "Open Settings", shortcut: "⌘," },
],
},
{
heading: "Actions",
commands: [
{ icon: PlusCircle, label: "Create New Document", shortcut: "⌘N" },
{ icon: Upload, label: "Upload File", shortcut: "⌘U" },
{ icon: Download, label: "Export Data", shortcut: "⌘E" },
{ icon: Share2, label: "Share Current Page", shortcut: "⌘⇧S" },
{ icon: Copy, label: "Copy Link", shortcut: "⌘⇧C" },
],
},
{
heading: "Navigation",
commands: [
{ icon: FileText, label: "Go to Documents" },
{ icon: Users, label: "Go to Team" },
{ icon: Bell, label: "Go to Notifications" },
{ icon: Settings, label: "Go to Settings" },
],
},
{
heading: "Preferences",
commands: [
{ icon: Moon, label: "Toggle Dark Mode", shortcut: "⌘⇧D" },
{ icon: Bell, label: "Notification Settings" },
{ icon: HelpCircle, label: "Help & Support", shortcut: "⌘?" },
],
},
{
heading: "Danger Zone",
commands: [
{ icon: Trash2, label: "Delete Current Item", shortcut: "⌘⌫" },
{ icon: LogOut, label: "Sign Out" },
],
},
];
export const title = "React Dialog Block Command Palette";
export default function DialogCommandPalette() {
const [open, setOpen] = useState(false);
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down);
}, []);
const handleSelect = (command: string) => {
console.log("Selected:", command);
setOpen(false);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<DialogTrigger asChild>
<Button variant="outline" className="gap-2">
<Search className="h-4 w-4" />
<span>Search commands...</span>
<kbd className="pointer-events-none ml-2 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span className="text-xs">⌘</span>K
</kbd>
</Button>
</DialogTrigger>
</div>
<DialogContent className="overflow-hidden p-0 sm:max-w-lg">
<DialogTitle className="sr-only">Command Palette</DialogTitle>
<CommandPrimitive className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[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-4 [&_[cmdk-item]_svg]:w-4">
<div className="flex items-center border-b px-3">
<Command className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandInput
placeholder="Type a command or search..."
className="flex h-12 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
<CommandList className="max-h-[400px] overflow-y-auto overflow-x-hidden">
<CommandEmpty>No results found.</CommandEmpty>
{commandGroups.map((group, groupIndex) => (
<div key={group.heading}>
<CommandGroup heading={group.heading}>
{group.commands.map((command) => (
<CommandItem
key={command.label}
onSelect={() => handleSelect(command.label)}
className="cursor-pointer"
>
<command.icon className="mr-2 h-4 w-4" />
<span>{command.label}</span>
{command.shortcut && (
<CommandShortcut>{command.shortcut}</CommandShortcut>
)}
</CommandItem>
))}
</CommandGroup>
{groupIndex < commandGroups.length - 1 && <CommandSeparator />}
</div>
))}
</CommandList>
<div className="border-t px-3 py-2">
<div className="flex items-center justify-between text-xs text-muted-foreground">
<div className="flex items-center gap-2">
<kbd className="rounded border bg-muted px-1.5 py-0.5">↑↓</kbd>
<span>Navigate</span>
</div>
<div className="flex items-center gap-2">
<kbd className="rounded border bg-muted px-1.5 py-0.5">↵</kbd>
<span>Select</span>
</div>
<div className="flex items-center gap-2">
<kbd className="rounded border bg-muted px-1.5 py-0.5">esc</kbd>
<span>Close</span>
</div>
</div>
</div>
</CommandPrimitive>
</DialogContent>
</Dialog>
);
}