299 lines
11 KiB
TypeScript
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 />
|
|
};
|