first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
# DropdownMenu
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - dropdown-menu.variants — `dropdownMenuContentVarian…
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
- dropdown-menu.variants — `dropdownMenuContentVarian…
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
- dropdown-menu.variants — `dropdownMenuContentVarian…
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
- dropdown-menu.variants — `dropdownMenuContentVarian…
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
- [dropdown-menu.variants](./dropdown-menu.variants.md) — `dropdownMenuContentVariants`, `dropdownMenuItemVariants`, `dropdownMenuLabelVariants`, `dropdownMenuSeparatorVariants`…
## Purpose
`<DropdownMenu>` and its sub-components (`<DropdownMenuTrigger>`, `<DropdownMenuContent>`, `<DropdownMenuItem>`, `<DropdownMenuGroup>`, `<DropdownMenuLabel>`, `<DropdownMenuSeparator>`, `<DropdownMenuShortcut>`, `<DropdownMenuCheckboxItem>`, `<DropdownMenuRadioGroup>`, `<DropdownMenuRadioItem>`, `<DropdownMenuSub>`, `<DropdownMenuSubTrigger>`, `<DropdownMenuSubContent>`, `<DropdownMenuPortal>`) are the chassis dropdown-menu primitive built on `@radix-ui/react-dropdown-menu`. Radix supplies the WAI-ARIA menu semantics (`role="menu"`, `role="menuitem"`, arrow-key roving focus, type-ahead search, Escape closes, focus-trap while open, restore-focus to the trigger on close), and the chassis layers cva slots, theme overrides, and a translated `<DropdownMenuShortcut>` `aria-keyshortcuts` attribute on top.
**Composition rule (shadcn skill rule):** items typically sit inside a `<DropdownMenuGroup>` so screen-readers announce them as one set. Single ungrouped items at the top level are also acceptable. Sub-menus pair `<DropdownMenuSubTrigger>` with `<DropdownMenuSubContent>` inside a `<DropdownMenuSub>`.
## Features
- WAI-ARIA menu semantics from Radix.
- Arrow-key roving focus + type-ahead search.
- Escape closes; click-outside dismisses; restore-focus to the trigger on close.
- Auto-portalled `<DropdownMenuContent>` (matches shadcn upstream — consumers do not need to remember `<DropdownMenuPortal>`).
- `<DropdownMenuShortcut>` accepts an `ariaKeyShortcutsKey` translation key so the localised shortcut announcement reaches assistive tech.
- `<DropdownMenuCheckboxItem>` / `<DropdownMenuRadioGroup>` / `<DropdownMenuRadioItem>` with 32-px gutter for the indicator.
- `<DropdownMenuSub>` for nested sub-menus with a chevron indicator (RTL-flipped via `rtl:rotate-180`).
- `inset` variant on `<DropdownMenuItem>` / `<DropdownMenuLabel>` / `<DropdownMenuSubTrigger>` aligns with checkbox/radio rows.
- All sub-components forward refs to their underlying Radix elements.
## Usage: Basic Example
```tsx
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@suaveplan/react-ui";
export function UserMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger>Open menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuGroup>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Sign out</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}
```
## Usage: Advanced Example
```tsx
import {
Button,
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@suaveplan/react-ui";
import { useState } from "react";
export function EditorMenu() {
const [showLineNumbers, setShowLineNumbers] = useState(true);
const [theme, setTheme] = useState<"light" | "dark">("light");
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">View</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Editor</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Save
<DropdownMenuShortcut ariaKeyShortcutsKey="app.shortcuts.save">
⌘S
</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem
checked={showLineNumbers}
onCheckedChange={(checked) =>
setShowLineNumbers(Boolean(checked))
}
>
Line numbers
</DropdownMenuCheckboxItem>
<DropdownMenuRadioGroup
value={theme}
onValueChange={(v) => setTheme(v as "light" | "dark")}
>
<DropdownMenuRadioItem value="light">
Light theme
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark">
Dark theme
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
<DropdownMenuSub>
<DropdownMenuSubTrigger>Export</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>PDF</DropdownMenuItem>
<DropdownMenuItem>HTML</DropdownMenuItem>
<DropdownMenuItem>Markdown</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>
);
}
```
## API Reference
### `<DropdownMenu>`
The Radix Root. Standard Radix Root props (`open`, `defaultOpen`, `onOpenChange`, `modal`, `dir`).
### `<DropdownMenuTrigger>`
Wraps the Radix Trigger (renders a `<button>` or polymorphically the child via `asChild`).
### `<DropdownMenuContent>`
The floating menu surface. Auto-portalled. Accepts standard Radix Content props (`side`, `align`, `sideOffset`, `alignOffset`, `collisionPadding`, etc.).
### `<DropdownMenuItem>`
A single menu item.
| Prop | Type | Default | Description |
|---|---|---|---|
| `inset` | `boolean` | `false` | Adds 32-px left gutter. |
| `disabled` | `boolean` | `false` | Skips in keyboard nav and ignores activation. |
| `onSelect` | `(event: Event) => void` | _none_ | Fires on activation. |
### `<DropdownMenuGroup>`, `<DropdownMenuLabel>`, `<DropdownMenuSeparator>`
Standard menu structure. `<DropdownMenuLabel>` accepts an `inset` prop for alignment.
### `<DropdownMenuShortcut>`
Keyboard-shortcut hint pinned at the end of an item.
| Prop | Type | Default | Description |
|---|---|---|---|
| `ariaKeyShortcutsKey` | `TranslationKey` | _none_ | When present, sets `aria-keyshortcuts` to the translated shortcut. |
### `<DropdownMenuCheckboxItem>`, `<DropdownMenuRadioGroup>`, `<DropdownMenuRadioItem>`
Standard Radix props (`checked`, `onCheckedChange`, `value`, `onValueChange`).
### `<DropdownMenuSub>`, `<DropdownMenuSubTrigger>`, `<DropdownMenuSubContent>`
Nested sub-menu. `<DropdownMenuSubTrigger>` accepts an `inset` prop and renders a chevron icon (RTL-flipped via `rtl:rotate-180`).
## Implementation Notes
- The chassis NEVER assigns a numeric `z-index` to the menu — Radix portals handle stacking.
- The check / dot indicators sit at `start-2` (logical property) so they flip correctly in RTL contexts.
- `motion-reduce:animate-none` collapses the open/close transition for `prefers-reduced-motion: reduce`.
- The `<DropdownMenuShortcut>` separates the visible text (passed as children) from the localised announcement (`ariaKeyShortcutsKey`) so a glyph like `⌘S` can be shown to sighted users while screen-readers hear "Command S" or the localised equivalent.
- Per the shadcn skill rule, items in groups MUST live inside `<DropdownMenuGroup>` for correct screen-reader announcement.
- `<DropdownMenuContent>` auto-portals via `<DropdownMenuPortal>` so consumers do not have to remember to wrap manually — matches shadcn upstream's convention.