Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions components/retroui/Command.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"use client";

import React from "react";
import { cn } from "@/lib/utils";
import { Dialog } from "@/components/retroui/Dialog";
import { type DialogProps } from "@radix-ui/react-dialog";
import { Command as CommandPrimitive } from "cmdk";
import { Check, LucideIcon, Search } from "lucide-react";

function Command({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive>) {
return (
<CommandPrimitive
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-background text-foreground border-border shadow-md",
className,
)}
{...props}
/>
);
}

type CommandDialogProps = DialogProps & { className?: string };

const CommandDialog = ({
children,
className,
...props
}: CommandDialogProps) => {
return (
<Dialog {...props}>
<Dialog.Content
className={cn(
"overflow-hidden p-0 shadow-lg w-full max-w-md",
className,
)}
>
<Command 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-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</Dialog.Content>
</Dialog>
);
};

function CommandInput({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
return (
<div
className="flex items-center border-border border-b px-3"
cmdk-input-wrapper=""
data-slot="command-input"
>
<Search className="me-2 h-4 w-4 shrink-0 opacity-50 text-foreground" />
<CommandPrimitive.Input
className={cn(
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden text-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 font-body",
className,
)}
{...props}
/>
</div>
);
}

function CommandList({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.List>) {
return (
<CommandPrimitive.List
data-slot="command-list"
className={cn(
"max-h-[400px] overflow-auto overscroll-contain transition-[height] h-[calc(min(300px,var(--cmdk-list-height)))] bg-background",
className,
)}
{...props}
/>
);
}

function CommandEmpty({
...props
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
return (
<CommandPrimitive.Empty
data-slot="command-empty"
className="py-6 text-center text-sm text-muted-foreground font-body"
{...props}
/>
);
}

function CommandGroup({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
return (
<CommandPrimitive.Group
data-slot="command-group"
className={cn(
"overflow-hidden p-1.5 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 font-body",
className,
)}
{...props}
/>
);
}

function CommandSeparator({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
return (
<CommandPrimitive.Separator
data-slot="command-separator"
className={cn("h-px bg-border", className)}
{...props}
/>
);
}

function CommandItem({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
return (
<CommandPrimitive.Item
data-slot="command-item"
className={cn(
"relative flex text-foreground cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected=true]:bg-primary data-[selected=true]:text-primary-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground transition-all font-body",
className,
)}
{...props}
/>
);
}

const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
data-slot="command-shortcut"
className={cn(
"ms-auto text-xs tracking-widest text-muted-foreground font-body",
className,
)}
{...props}
/>
);
};

interface CommandCheckProps {
icon?: LucideIcon;
className?: string;
}

function CommandCheck({
icon: Icon = Check,
className,
...props
}: CommandCheckProps) {
return (
<Icon
data-slot="command-check"
data-check="true"
className={cn("size-4 ms-auto text-primary", className)}
{...props}
/>
);
}

const CommandComponent = Object.assign(Command, {
Check: CommandCheck,
Dialog: CommandDialog,
Empty: CommandEmpty,
Group: CommandGroup,
Input: CommandInput,
Item: CommandItem,
List: CommandList,
Separator: CommandSeparator,
Shortcut: CommandShortcut,
});

export { CommandComponent as Command };
3 changes: 2 additions & 1 deletion components/retroui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export * from "./Sonner";
export * from "./Tooltip";
export * from "./Breadcrumb";
export * from "./CommandDisplay";
export * from "./ContextMenu";
export * from "./Command";
export * from "./Loader";
export * from "./ContextMenu";
16 changes: 15 additions & 1 deletion config/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ export const componentConfig: {
name: "breadcrumb",
filePath: "components/retroui/Breadcrumb.tsx",
},
command: {
name: "command",
filePath: "components/retroui/Command.tsx",
},
"context-menu": {
name: "context-menu",
filePath: "components/retroui/ContextMenu.tsx",
Expand Down Expand Up @@ -604,6 +608,16 @@ export const componentConfig: {
() => import("@/preview/components/breadcrumb-link-component"),
),
},
"command-style-default": {
name: "command-style-default",
filePath: "preview/components/command-style-default.tsx",
preview: lazy(() => import("@/preview/components/command-style-default")),
},
"command-style-dialog": {
name: "command-style-dialog",
filePath: "preview/components/command-style-dialog.tsx",
preview: lazy(() => import("@/preview/components/command-style-dialog")),
},
"context-menu-style-default": {
name: "context-menu-style-default",
filePath: "preview/components/context-menu-style-default.tsx",
Expand Down Expand Up @@ -634,7 +648,7 @@ export const componentConfig: {
"loader-style-custom": {
name: "loader-style-custom",
filePath: "preview/components/loader-style-custom.tsx",
preview: lazy(() => import("@/preview/components/loader-style-custom")),
preview: lazy(() => import("@/preview/components/loader-style-custom"))
},
},
};
1 change: 1 addition & 0 deletions config/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const navConfig: INavigationConfig = {
{ title: "Button", href: `${componentsRoute}/button` },
{ title: "Card", href: `${componentsRoute}/card` },
{ title: "Checkbox", href: `${componentsRoute}/checkbox` },
{ title: "Command", href: `${componentsRoute}/command` },
{ title: "Dialog", href: `${componentsRoute}/dialog` },
{ title: "Input", href: `${componentsRoute}/input` },
{ title: "Label", href: `${componentsRoute}/label` },
Expand Down
40 changes: 40 additions & 0 deletions content/docs/components/command.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Command
description: A command palette component for quick navigation and actions
lastUpdated: 21 Aug, 2025
links:
source: https://github.com/Logging-Stuff/RetroUI/blob/main/components/retroui/Command.tsx
---

<ComponentShowcase name="command-style-default" />
<br />
<br />

<ComponentInstall>
<ComponentInstall.Cli npmCommand="npx shadcn@latest add &quot;https://retroui.dev/r/command.json&quot;" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Use real double quotes via JSX braces instead of HTML entities in CLI command

Avoid &quot; in JSX props; it may copy as literal text. Use a JS string so copied command contains actual quotes across all shells.

-  <ComponentInstall.Cli npmCommand="npx shadcn@latest add &quot;https://retroui.dev/r/command.json&quot;" />
+  <ComponentInstall.Cli npmCommand={'npx shadcn@latest add "https://retroui.dev/r/command.json"'} />

Run this quick check to catch any remaining HTML-entity quotes in MDX props:


🏁 Script executed:

#!/bin/bash
rg -nP '&quot;' content/docs/**/*.mdx

Length of output: 1374


Refactor CLI command to use real quotes instead of HTML entities

Verified via ripgrep that this is the only occurrence of &quot; in MDX props. Please update the following line in content/docs/components/command.mdx:

-  <ComponentInstall.Cli npmCommand="npx shadcn@latest add &quot;https://retroui.dev/r/command.json&quot;" />
+  <ComponentInstall.Cli npmCommand={'npx shadcn@latest add "https://retroui.dev/r/command.json"'} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ComponentInstall.Cli npmCommand="npx shadcn@latest add &quot;https://retroui.dev/r/command.json&quot;" />
<ComponentInstall.Cli npmCommand={'npx shadcn@latest add "https://retroui.dev/r/command.json"'} />
🤖 Prompt for AI Agents
In content/docs/components/command.mdx around line 14, the npmCommand prop uses
HTML entity &quot; for the internal URL quotes; replace the entities with real
double quotes and make the prop use single quotes for its value so it remains
valid JSX/MDX, e.g. change the attribute to use single quotes around the whole
string and real double quotes around the URL.

<ComponentInstall.Manual>
#### 1. Install dependencies:

```sh
npm install class-variance-authority cmdk
```

<br />

#### 2. Copy the code 👇 into your project:

<ComponentSource name="command" />

</ComponentInstall.Manual>
</ComponentInstall>

<br />
<br />

## Examples

### Dialog

<ComponentShowcase name="command-style-dialog" />
<br />
<br />
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@vercel/speed-insights": "^1.2.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"contentlayer": "^0.3.4",
"date-fns": "^4.1.0",
"lucide-react": "^0.445.0",
Expand Down
56 changes: 56 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading