Skip to content

Commit 767429b

Browse files
committed
feat: add Command and Dialog components with associated UI elements
- Introduced Command component for command palette functionality. - Added CommandDialog for displaying commands in a dialog format. - Implemented CommandInput, CommandList, CommandEmpty, CommandGroup, CommandSeparator, CommandItem, and CommandShortcut for enhanced command interactions. - Created Dialog component with DialogTrigger, DialogPortal, DialogClose, DialogOverlay, DialogContent, DialogHeader, DialogFooter, DialogTitle, and DialogDescription for modal functionality. - Added Label component for form labeling. - Updated package dependencies to include @radix-ui/react-dialog, @radix-ui/react-label, and cmdk.
1 parent 95fe342 commit 767429b

File tree

8 files changed

+1057
-30
lines changed

8 files changed

+1057
-30
lines changed

app/desktop/page.tsx

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,76 @@
1+
'use client'
2+
13
import { GithubIcon } from "@/components/icons/github";
24
import { SearchIcon } from "@/components/icons/search";
35
import { VisualStudioCodeIcon } from "@/components/icons/vscode";
46
import { WindowsIcon } from "@/components/icons/windows";
57
import { cn } from "@/lib/utils";
68
import Image from "next/image";
9+
import { Windows } from "./windows";
10+
import { useEffect, useState } from "react";
711

8-
const menuItems = [
9-
{
10-
name: 'Windows',
11-
href: '#',
12-
icon: WindowsIcon,
13-
className: '',
14-
},
15-
{
16-
name: 'Search',
17-
href: '#',
18-
icon: SearchIcon,
19-
className: 'text-gray-500 size-6',
20-
},
21-
{
22-
name: 'GitHub',
23-
href: '#',
24-
icon: GithubIcon,
25-
className: 'text-gray-600 size-7',
26-
},
27-
{
28-
name: 'VS Code',
29-
href: '#',
30-
icon: VisualStudioCodeIcon,
31-
className: 'text-blue-500 size-7 mt-0.5',
32-
},
33-
];
12+
export default function HomePage() {
13+
const [openWindows, setOpenWindows] = useState(false);
3414

15+
// Ctrl + k show setOpenWindows(true)
16+
useEffect(() => {
17+
const handleKeyDown = (e: KeyboardEvent) => {
18+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
19+
e.preventDefault();
20+
setOpenWindows(true);
21+
}
22+
};
23+
window.addEventListener('keydown', handleKeyDown);
24+
return () => window.removeEventListener('keydown', handleKeyDown);
25+
}, []);
26+
27+
const menuItems = [
28+
{
29+
name: 'Windows',
30+
href: '#',
31+
icon: WindowsIcon,
32+
className: '',
33+
onClick: () => console.log("Open Window"),
34+
},
35+
{
36+
name: 'Search',
37+
href: '#',
38+
icon: SearchIcon,
39+
className: 'text-gray-500 size-6',
40+
onClick: () => console.log('Search clicked'),
41+
},
42+
{
43+
name: 'GitHub',
44+
href: '#',
45+
icon: GithubIcon,
46+
className: 'text-gray-600 size-7',
47+
onClick: () => console.log('GitHub clicked'),
48+
},
49+
{
50+
name: 'VS Code',
51+
href: '#',
52+
icon: VisualStudioCodeIcon,
53+
className: 'text-blue-500 size-7 mt-0.5',
54+
onClick: () => console.log('VS Code clicked'),
55+
},
56+
];
3557

36-
export default function HomePage() {
3758
return (
3859
<main className="min-h-screen relative bg-linear-to-b from-amber-50 to-amber-100 text-center">
3960
<Image src="/angkor-wat-cambodia.jpg" alt="Background" fill className="absolute pointer-events-none inset-0 object-cover" />
61+
4062
<footer className="fixed bottom-0 inset-x-0 border-t backdrop-blur-sm border-black/5 bg-white/90">
4163
<ul className="flex w-full max-w-3xl mx-auto justify-center py-1.5 gap-1">
4264
{menuItems.map((item) => (
4365
<li key={item.name}>
44-
<a href={item.href} className="relative flex items-center justify-center size-9 rounded-lg hover:bg-white hover:shadow-lg duration-400 shadow-black/10 transition-colors">
66+
<button type="button" onClick={item.onClick} className="relative flex cursor-pointer items-center justify-center size-9 rounded-lg hover:bg-white hover:shadow-lg duration-400 shadow-black/10 transition-colors">
4567
<item.icon className={cn('size-7 text-gray-600', item.className )} />
46-
</a>
68+
</button>
4769
</li>
4870
))}
4971
</ul>
5072
</footer>
73+
<Windows open={openWindows} onOpenChange={setOpenWindows} />
5174
</main>
5275
);
5376
}

app/desktop/windows.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { FilesIcon, UserCircle } from "lucide-react"
2+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "@/components/ui/command"
3+
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog"
4+
import { GithubIcon } from "@/components/icons/github";
5+
import { VisualStudioCodeIcon } from "@/components/icons/vscode";
6+
interface WindowsProps {
7+
open: boolean;
8+
onOpenChange: (open: boolean) => void;
9+
}
10+
11+
export function Windows({ open, onOpenChange }: WindowsProps) {
12+
return (
13+
<Dialog open={open} onOpenChange={onOpenChange}>
14+
<DialogContent className="overflow-hidden p-0">
15+
<DialogTitle className="hidden">Open Window</DialogTitle>
16+
<DialogDescription className="hidden"> Open the command menu to quickly navigate and execute commands.</DialogDescription>
17+
<Command className="**:[[cmdk-group-heading]]:px-2 font-sans **:[[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-5 [&_[cmdk-item]_svg]:w-5">
18+
<CommandInput placeholder="Type a command or search..." />
19+
<CommandList>
20+
<CommandEmpty>No results found.</CommandEmpty>
21+
<CommandGroup heading="Suggestions">
22+
<CommandItem>
23+
<GithubIcon />
24+
<span>Github</span>
25+
</CommandItem>
26+
<CommandItem>
27+
<VisualStudioCodeIcon />
28+
<span>VS Code</span>
29+
</CommandItem>
30+
</CommandGroup>
31+
<CommandSeparator />
32+
<CommandGroup heading="About Me">
33+
<CommandItem>
34+
<UserCircle />
35+
<span>Profile</span>
36+
<CommandShortcut>⌘P</CommandShortcut>
37+
</CommandItem>
38+
<CommandItem>
39+
<FilesIcon />
40+
<span>Cover Letter</span>
41+
<CommandShortcut>⌘C</CommandShortcut>
42+
</CommandItem>
43+
</CommandGroup>
44+
</CommandList>
45+
</Command>
46+
</DialogContent>
47+
</Dialog>
48+
)
49+
}

components/ui/button.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as React from "react"
2+
import { Slot } from "@radix-ui/react-slot"
3+
import { cva, type VariantProps } from "class-variance-authority"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const buttonVariants = cva(
8+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9+
{
10+
variants: {
11+
variant: {
12+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
13+
destructive:
14+
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15+
outline:
16+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17+
secondary:
18+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
19+
ghost:
20+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21+
link: "text-primary underline-offset-4 hover:underline",
22+
},
23+
size: {
24+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
25+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27+
icon: "size-9",
28+
"icon-sm": "size-8",
29+
"icon-lg": "size-10",
30+
},
31+
},
32+
defaultVariants: {
33+
variant: "default",
34+
size: "default",
35+
},
36+
}
37+
)
38+
39+
function Button({
40+
className,
41+
variant = "default",
42+
size = "default",
43+
asChild = false,
44+
...props
45+
}: React.ComponentProps<"button"> &
46+
VariantProps<typeof buttonVariants> & {
47+
asChild?: boolean
48+
}) {
49+
const Comp = asChild ? Slot : "button"
50+
51+
return (
52+
<Comp
53+
data-slot="button"
54+
data-variant={variant}
55+
data-size={size}
56+
className={cn(buttonVariants({ variant, size, className }))}
57+
{...props}
58+
/>
59+
)
60+
}
61+
62+
export { Button, buttonVariants }

0 commit comments

Comments
 (0)