Files
cadence-ui/apps/docs/src/components/command.stories.tsx
T

299 lines
11 KiB
TypeScript

import { useState } from "react";
import {
Button,
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut
} from "@ai-ui/ui";
import type { Meta, StoryObj } from "@storybook/react";
const inlineItems = [
{
heading: "Navigation",
items: [
{ label: "Open docs", shortcut: "G D", value: "open-docs" },
{ label: "Go to releases", shortcut: "G R", value: "go-releases" }
]
},
{
heading: "Actions",
items: [
{ label: "Publish update", shortcut: "P U", value: "publish-update" },
{ label: "Invite reviewer", shortcut: "I R", value: "invite-reviewer" }
]
}
];
function InlineCommandShowcase() {
return (
<Command className="w-[520px]">
<CommandInput placeholder="Search across the workspace" />
<CommandList>
<CommandEmpty>No matching actions.</CommandEmpty>
{inlineItems.map((group, index) => (
<div key={group.heading}>
<CommandGroup heading={group.heading}>
{group.items.map((item) => (
<CommandItem key={item.value} value={item.value}>
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">{item.label}</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
{item.value === "open-docs"
? "Jump into authoring guidance, slots, and motion references."
: item.value === "go-releases"
? "Open the current release desk and recent launch notes."
: item.value === "publish-update"
? "Queue the current workspace update for approval."
: "Assign the next reviewer without leaving the palette."}
</span>
</span>
<CommandShortcut>{item.shortcut}</CommandShortcut>
</CommandItem>
))}
</CommandGroup>
{index < inlineItems.length - 1 ? <CommandSeparator /> : null}
</div>
))}
</CommandList>
</Command>
);
}
function OperationsWorkbenchShowcase() {
return (
<Command
className="w-[560px]"
footer={
<div className="flex items-center justify-between gap-3">
<p className="m-0 text-xs text-[var(--color-muted-foreground)]">
Tip: press <kbd className="rounded border px-1.5 py-0.5">Enter</kbd> to run the highlighted action.
</p>
<Button size="sm" variant="ghost">
Manage actions
</Button>
</div>
}
label="Operations workbench"
loop
>
<CommandInput placeholder="Search releases, docs, and recent work" />
<CommandList>
<CommandEmpty>No matching actions.</CommandEmpty>
<CommandGroup heading="Recent">
<CommandItem keywords={["launch", "release"]} value="recent-launch-review">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Launch review</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Reopen the last routed launch brief and approvals queue.
</span>
</span>
<CommandShortcut>R</CommandShortcut>
</CommandItem>
<CommandItem keywords={["blocked", "queue"]} value="recent-blocked-queue">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Blocked queue</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Inspect stalled items, owners, and next unblock steps.
</span>
</span>
<CommandShortcut>B</CommandShortcut>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Suggestions">
<CommandItem keywords={["docs", "stories"]} value="docs-storybook">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Open Storybook docs</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Review the live component contract and interaction stories.
</span>
</span>
<CommandShortcut>G D</CommandShortcut>
</CommandItem>
<CommandItem disabled value="compliance-review">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Compliance review locked</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
This action unlocks after policy artifacts finish syncing.
</span>
</span>
<CommandShortcut>Locked</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
);
}
function DialogCommandShowcase() {
const [open, setOpen] = useState(false);
return (
<div className="flex justify-center">
<Button onClick={() => setOpen(true)}>Open command palette</Button>
<CommandDialog
description="Jump to docs, recent launches, and operational shortcuts without leaving the current view."
footer={
<div className="flex items-center justify-between gap-3">
<p className="m-0 text-xs text-[var(--color-muted-foreground)]">
Need a new action? Add it to the workspace command registry.
</p>
<Button size="sm" variant="ghost">
Manage shortcuts
</Button>
</div>
}
onOpenChange={setOpen}
open={open}
title="Workspace command palette"
>
<CommandInput placeholder="Type a command or search" />
<CommandList>
<CommandEmpty>No commands available.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem value="launch-checklist">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Launch checklist</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Open the current milestone handoff and owner checklist.
</span>
</span>
</CommandItem>
<CommandItem value="rollout-audit">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Rollout audit</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Review timing, communications, and incident readiness.
</span>
</span>
</CommandItem>
<CommandItem value="brand-theme">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Brand theme tokens</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Jump to the token surface that controls tonal palette shifts.
</span>
</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="People">
<CommandItem value="jordan-lee">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Jordan Lee</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Product lead for launch sequencing and review coordination.
</span>
</span>
<CommandShortcut>@</CommandShortcut>
</CommandItem>
<CommandItem value="avery-carter">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Avery Carter</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Owns release notes, docs framing, and approval summaries.
</span>
</span>
<CommandShortcut>@</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
</div>
);
}
function LoadingResultsShowcase() {
return (
<Command
className="w-[520px]"
footer={
<div className="flex items-center justify-between gap-3">
<p className="m-0 text-xs text-[var(--color-muted-foreground)]">
Cached suggestions stay visible while the workspace search refreshes.
</p>
<Button size="sm" variant="ghost">
View queue
</Button>
</div>
}
label="Remote workspace search"
loading
loadingMessage="Searching remote workspace actions…"
>
<CommandInput placeholder="Search actions from all workspaces" />
<CommandList>
<CommandEmpty>No commands available.</CommandEmpty>
<CommandGroup heading="Cached suggestions">
<CommandItem value="cached-launch-checklist">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Launch checklist</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Reopen the previous launch checklist while fresh results stream in.
</span>
</span>
</CommandItem>
<CommandItem value="cached-review-queue">
<span className="grid min-w-0 flex-1 gap-0.5">
<span className="truncate font-medium">Review queue</span>
<span className="text-xs leading-5 text-[var(--color-muted-foreground)]">
Keep the last known reviewer list visible during the remote refresh.
</span>
</span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
);
}
const meta = {
title: "Components/Command",
component: InlineCommandShowcase,
parameters: {
docs: {
description: {
component:
"Command is the system surface for in-app palettes and quick action search. The highlighted row should feel adhesive and spatial, with enough motion to clarify focus and loading state without turning the palette into a busy animation surface."
}
},
layout: "centered"
},
tags: ["autodocs"]
} satisfies Meta<typeof InlineCommandShowcase>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Playground: Story = {
render: () => <InlineCommandShowcase />
};
export const OperationsWorkbench: Story = {
render: () => <OperationsWorkbenchShowcase />
};
export const DialogPalette: Story = {
render: () => <DialogCommandShowcase />
};
export const LoadingResults: Story = {
parameters: {
docs: {
description: {
story:
"Loading should not feel like a hard swap. Keep a soft status band at the top, let cached results dim underneath, and preserve the same highlighted-row motion language."
}
}
},
render: () => <LoadingResultsShowcase />
};