# Dropdown Menu

A dropdown menu with panel fade, scale, and lift.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).

## Installation

<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>

```bash
npx shadcn@latest add @framecn/dropdown-menu
```

<Step>Copy and paste the following code into your project.</Step>

<Step>Update the import paths to match your project setup.</Step>

## Usage

```tsx
import { DropdownMenu } from "@/components/ui/dropdown-menu";
```

```tsx
<DropdownMenu label="Options" state="opened" />
```

## Smooth transitions

The docs preview animates an outline trigger press, opens the panel, walks row 1 through hover → press → idle, then closes. Drive the trigger with `useButtonTransition`, the panel with `useDropdownMenuTransition`, and row states with `useDropdownMenuItemTransition`:

```tsx
import { useCurrentState } from "@/lib/framecn-ui";
import { DropdownMenu } from "@/components/framecn/dropdown-menu";
import { useDropdownMenuTransition } from "@/components/framecn/use-dropdown-menu-transition";
import { useDropdownMenuItemTransition } from "@/components/framecn/use-dropdown-menu-item-transition";
import { useButtonTransition } from "@/components/framecn/use-button-transition";

const triggerStyle = useButtonTransition(
  [
    { at: 14, state: "hover" },
    { at: 26, state: "press" },
  ],
  { variant: "outline" }
);

const menu = useDropdownMenuTransition([
  { at: 32, state: "opened", duration: 16 },
  { at: 96, state: "closed", duration: 12 },
]);

const rowState = useCurrentState(
  [
    { at: 52, state: "hover" },
    { at: 70, state: "press" },
    { at: 82, state: "idle" },
  ],
  "idle"
);
const row = useDropdownMenuItemTransition([{ at: 0, state: rowState }]);

<DropdownMenu
  label="Options"
  style={menu}
  triggerStyle={triggerStyle}
  itemStyles={[undefined, row, undefined, undefined]}
/>;
```

`style` takes precedence over `state` when both are provided. `itemStyles` is indexed by row.

## API Reference

### DropdownMenu

| Prop               | Type                                     | Default                                         |
| ------------------ | ---------------------------------------- | ----------------------------------------------- |
| `state`            | `"opened" \| "closed"`                   | `"closed"`                                      |
| `style`            | `DropdownMenuStyle`                      | `-` (takes precedence over `state`)             |
| `label`            | `string`                                 | `"Options"`                                     |
| `items`            | `string[]`                               | `["Profile", "Billing", "Settings", "Log out"]` |
| `highlightedIndex` | `number`                                 | `-1`                                            |
| `pressedIndex`     | `number`                                 | `-1`                                            |
| `itemStyles`       | `(DropdownMenuItemStyle \| undefined)[]` | `-` (per-row override, indexed by row)          |
| `triggerStyle`     | `ButtonStyle`                            | `-`                                             |
| `theme`            | `Partial<FramecnTheme>`                  | `-`                                             |
| `className`        | `string`                                 | `-`                                             |