first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user