# Command

A searchable list of pages and actions, inline or as a command palette in a dialog.

```tsx
import { BellIcon, FileTextIcon, PlusIcon, ServerIcon, ShieldAlertIcon } from "lucide-react"
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "@zenoxai/ui/components/command"

function CommandDemo() {
  return (
    <Command className="max-w-sm rounded-lg border border-border">
      <CommandInput placeholder="Search pages and actions" />
      <CommandList>
        <CommandEmpty>No results.</CommandEmpty>
        <CommandGroup heading="Pages">
          <CommandItem>
            <BellIcon />
            Alerts
          </CommandItem>
          <CommandItem>
            <ServerIcon />
            Assets
          </CommandItem>
          <CommandItem>
            <ShieldAlertIcon />
            Vulnerabilities
          </CommandItem>
          <CommandItem>
            <FileTextIcon />
            Reports
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Actions">
          <CommandItem>
            <PlusIcon />
            New investigation
            <CommandShortcut>⌘I</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <FileTextIcon />
            Export weekly report
            <CommandShortcut>⌘E</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}
```

## Usage

```tsx
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@zenoxai/ui/components/command"
```

```tsx
<Command>
  <CommandInput placeholder="Search pages and actions" />
  <CommandList>
    <CommandEmpty>No results.</CommandEmpty>
    <CommandGroup heading="Pages">
      <CommandItem onSelect={() => navigate("/alerts")}>Alerts</CommandItem>
      <CommandItem onSelect={() => navigate("/assets")}>Assets</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>
```

`Command` is built on [cmdk](https://cmdk.paco.me). It filters and ranks its items as the user
types, moves the highlight with the arrow keys and runs `onSelect` on Enter or click. Use it for
long lists of commands or destinations that people find faster by typing than by scanning: a
command palette, a "go to" list, a picker with dozens of entries.

- For a few actions behind a button, use a [Dropdown Menu](/components/dropdown-menu).
- For a value in a form, use a [Combobox](/components/combobox): it has the field semantics a
  form needs.

Items match on their text. When the text is not what people type, or two items share it, pass
a `value` (and `keywords` for synonyms). `CommandEmpty` shows when nothing matches.
`CommandShortcut` only displays a shortcut; bind the key yourself.

When it mounts, `Command` scrolls its first item into view, and that scrolls the page too. Place
an inline `Command` where it is visible on load, or inside an overlay that mounts it on open, as
in the examples below.

## Examples

### Command palette

`CommandDialog` puts a `Command` in a [Dialog](/components/dialog), placed in the upper third
of the screen. It has no trigger part: control `open` and open it from a button, a keyboard
shortcut, or both. Close it in each item's `onSelect`. `title` and `description` label the
dialog for screen readers and are not shown.

```tsx
import * as React from "react"
import { BellIcon, FileTextIcon, PlusIcon, SearchIcon, ServerIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "@zenoxai/ui/components/command"

function CommandInDialog() {
  const [open, setOpen] = React.useState(false)

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        <SearchIcon data-icon="inline-start" />
        Search
      </Button>
      <CommandDialog
        open={open}
        onOpenChange={setOpen}
        title="Command palette"
        description="Search for a page or an action"
      >
        <Command>
          {/* Each item's onSelect navigates or runs its action, then closes. */}
          <CommandInput placeholder="Type a command or search" />
          <CommandList>
            <CommandEmpty>No results.</CommandEmpty>
            <CommandGroup heading="Pages">
              <CommandItem onSelect={() => setOpen(false)}>
                <BellIcon />
                Alerts
              </CommandItem>
              <CommandItem onSelect={() => setOpen(false)}>
                <ServerIcon />
                Assets
              </CommandItem>
              <CommandItem onSelect={() => setOpen(false)}>
                <FileTextIcon />
                Reports
              </CommandItem>
            </CommandGroup>
            <CommandSeparator />
            <CommandGroup heading="Actions">
              <CommandItem onSelect={() => setOpen(false)}>
                <PlusIcon />
                New investigation
              </CommandItem>
            </CommandGroup>
          </CommandList>
        </Command>
      </CommandDialog>
    </>
  )
}
```

### In a popover

For a picker attached to a button, such as the assignee of an alert, put a `Command` in a
[Popover](/components/popover) with `p-0` on the content. Set `data-checked` on the current
item to show a check mark at its end, and close the popover in `onSelect`. When the value
belongs to a form field, use a [Combobox](/components/combobox) instead.

```tsx
import * as React from "react"
import { UserIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@zenoxai/ui/components/command"
import { Popover, PopoverContent, PopoverTrigger } from "@zenoxai/ui/components/popover"

function CommandInPopover() {
  const [open, setOpen] = React.useState(false)
  const [assignee, setAssignee] = React.useState("Marina Santos")
  const analysts = ["Marina Santos", "Jordan Lee", "Ada Okafor", "Thanh Nguyen"]

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger render={<Button variant="outline" />}>
        <UserIcon data-icon="inline-start" />
        {assignee}
      </PopoverTrigger>
      <PopoverContent align="start" className="w-60 p-0">
        <Command>
          <CommandInput placeholder="Assign to" />
          <CommandList>
            <CommandEmpty>No analyst found.</CommandEmpty>
            <CommandGroup heading="Analysts">
              {analysts.map((name) => (
                <CommandItem
                  key={name}
                  data-checked={assignee === name}
                  onSelect={() => {
                    setAssignee(name)
                    setOpen(false)
                  }}
                >
                  {name}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  )
}
```

## Accessibility

- The input is a combobox that controls the list. Arrow keys move the highlight, Enter selects
  and the highlighted item is announced; focus stays in the input.
- Give `CommandDialog` a `title` that names its purpose ("Command palette", "Go to page"). It is
  the dialog's accessible name.
- Group headings are announced as the names of their groups.

## API reference

`import { ... } from "@zenoxai/ui/components/command"`

### Command

No props of its own.

### CommandDialog

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| actionsRef | `RefObject<DialogRootActions \| null>` |  | A ref to imperative actions. - `unmount`: Ends the closing phase of the dialog after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the dialog completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the dialog imperatively when called. |
| defaultOpen | `boolean` | `false` | Whether the dialog is initially open.  To render a controlled dialog, use the `open` prop instead. |
| defaultTriggerId | `string \| null` |  | ID of the trigger that the dialog is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open dialog. |
| description | `string` | `Search for a command to run` | Accessible description, visually hidden; pass a translated one. |
| disablePointerDismissal | `boolean` | `false` | Whether to prevent the dialog from closing on outside presses. For non-modal dialogs, this also prevents the dialog from closing when focus moves outside of it. |
| handle | `DialogHandle<unknown>` |  | A handle to associate the dialog with a trigger. If specified, allows external triggers to control the dialog's open state. Can be created with the Dialog.createHandle() method. |
| modal | `boolean \| "trap-focus"` | `true` | Determines if the dialog enters a modal state when open. - `true`: user interaction is limited to just the dialog: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the dialog, but document page scroll is not locked and pointer interactions outside of it remain enabled.  When `modal` is `true` or `'trap-focus'`, render `<Dialog.Close>` inside `<Dialog.Popup>` so touch screen readers can escape the popup. |
| onOpenChange | `((open: boolean, eventDetails: DialogRootChangeEventDetails) => void)` |  | Event handler called when the dialog is opened or closed. |
| onOpenChangeComplete | `((open: boolean) => void)` |  | Event handler called after any animations complete when the dialog is opened or closed. |
| open | `boolean` |  | Whether the dialog is currently open. |
| showCloseButton | `boolean` | `false` | Shows the X button at the top-right corner; Escape closes it anyway. |
| title | `string` | `Command palette` | Accessible name of the dialog, visually hidden; pass a translated one. |
| triggerId | `string \| null` |  | ID of the trigger that the dialog is associated with. This is useful in conjunction with the `open` prop to create a controlled dialog. There's no need to specify this prop when the dialog is uncontrolled (that is, when the `open` prop is not set). |

### CommandInput

No props of its own.

### CommandList

No props of its own.

### CommandEmpty

No props of its own.

### CommandGroup

No props of its own.

### CommandItem

No props of its own.

### CommandShortcut

No props of its own.

### CommandSeparator

No props of its own.

