# 下拉菜单 · DropdownMenu

- 页面: https://jiguang132.com/zh/resources/astryx/docs/dropdown-menu
- 资源: Astryx (https://jiguang132.com/zh/resources/astryx.md)
- 全部指南: https://jiguang132.com/zh/resources/astryx/docs.md

下拉菜单用于操作场景。下面的官方示例展示原生用法与状态，属性名保持上游 API。

## 安装与前置条件

```bash
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
```

需要 React 19+，按顺序导入基础 CSS 和主题，并设置 Theme Provider。参见[开始使用](/resources/astryx/docs/getting-started)。

## 示例与使用方式

- **下拉菜单 · 综合展示 / 分组 1**：首先体验这个示例，再查看代码理解组件的组合方式。
- **下拉菜单 · 综合展示 2**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 综合展示 3**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 交互示例 4**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 交互示例 5**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 分组 6**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 基础用法 7**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 交互示例 8**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 禁用状态 9**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 交互示例 10**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。
- **下拉菜单 · 交互示例 11**：可交互查看这一用法的行为，复制代码时保留它实际使用的属性与导入。

## API 与上游详细参考

优先使用 0.7.1 官方中文说明；上游尚未提供中文的条目保留英文。属性名、类型及默认值保持原生 API，不把示例状态名称当成组件属性。

## Dropdown Menu

A dropdown menu that displays a list of actionable items in a popup triggered by a button. Use to present action options as a next step in a process, or to offer contextual actions without cluttering the interface.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| button | DropdownMenuButtonProps | { label: 'Menu' } | Props for the trigger button (Button props except onClick). Mutually exclusive with `trigger`. |
| renderTrigger | (props: DropdownMenuTriggerProps) => ReactNode |  | Render the control the menu hangs off — an IconButton, a chip, an avatar, a list row — instead of the built-in Button. Spread the given props onto it: they carry the press model (a mouse opens on press-down, a held finger opens with the finger down), the keyboard opens, the toggle click, and `aria-haspopup`/`aria-expanded`/`aria-controls`/`id`; the menu is then named by that control through `aria-labelledby`. Mutually exclusive with `button`. |
| items * | DropdownMenuOption[] |  | Array of menu entries. Each entry is one of: an action item `{label, onClick?, href?, target?, rel?, icon?, description?, endContent?, isDisabled?, variant?, hasCloseOnSelect?, id?}` (`href` makes the row a real link, so a modified click keeps the browser's meaning; variant `"destructive"` renders it in the error color; `endContent` holds trailing content such as a keyboard-shortcut hint; `id` is the row's stable React key, needed only when the array reorders or filters), a divider `{type: "divider"}`, or a section `{type: "section", title?, id?, items: [...action items]}`. |
| presentation | 'popover' \| 'bottom-sheet' \| 'adaptive' | 'popover' | Presentation surface for data-driven items. 'popover' stays anchored, 'bottom-sheet' always renders the actions in a modal BottomSheet, and 'adaptive' uses a BottomSheet on compact coarse-pointer layouts while remaining anchored elsewhere. Compound children currently support popover only. |
| isMenuOpen | boolean |  | Controlled open state for the menu. Mounting with true renders the menu open without moving focus into it; focus moves to the first item only when the menu opens after mount. |
| onOpenChange | (isOpen: boolean) => void |  | Callback fired when the open state changes. |
| menuWidth | number \| string |  | Minimum width for the popover presentation. Length values may grow for content; intrinsic and CSS-wide keywords select the preferred inline size. Every form is capped to the available viewport space. Defaults to matching the trigger width up to that cap. |
| menuMaxHeight | number |  | Maximum height in pixels for the popover presentation, for a menu that must fit its rows. Lifts the default 300px cap; the viewport still bounds it. |
| header | ReactNode |  | Content above the rows and outside the menu itself — a filter field, a toggle. A menu holds only its rows, so a control that is not a row goes here or in `footer`, never among the rows. It keeps its own keys and presses. With a header or footer the popover is a modal dialog around the menu: Tab and Shift+Tab move through the header, the menu and the footer and wrap at either end, and Escape closes. The bottom sheet shows it above the actions. |
| footer | ReactNode |  | Content below the rows and outside the menu itself — a "Create" button. Tab from the menu moves into it, and Tab past its last control wraps to the start of the dialog (see `header`). The bottom sheet shows it below the actions. |
| placement | 'above' \| 'below' \| 'start' \| 'end' | 'below' | Popover placement relative to the trigger. Ignored by the bottom-sheet presentation. Logical: start/end resolve against the menu's own inherited direction (RTL mirrors). |
| alignment | 'start' \| 'center' \| 'end' | 'start' | Popover alignment along the placement axis. Ignored by the bottom-sheet presentation. Logical: start/end follow the menu's own inherited direction (RTL mirrors). |
| onClick | () => void |  | Callback fired for accepted trigger activation. The trailing click from the same press that light-dismissed the menu is ignored. |
| hasChevron | boolean | true | Whether to show a chevron icon on the trigger button. Set to false for icon-only triggers. |
| children | ReactNode |  | Compound-mode menu content: DropdownMenuItem, DropdownMenuDivider, DropdownMenuSubMenu (a flyout on a laptop, a drilled-in view with a Back row on a phone), and the selectable items. Mutually exclusive with `items`. |

### 使用建议

- Keep menu items concise and action-oriented so users can scan options quickly.
- Use sections and dividers to group related actions when the menu has many items.
- For a short, flat action set, use presentation="bottom-sheet" when product policy calls for a modal touch surface.
- Use presentation="adaptive" when the same short action set should remain anchored on pointer layouts and become a BottomSheet on compact coarse-pointer layouts.
- For a hierarchy that cannot fit as adjacent flyouts on a compact touch surface, a product may explicitly use a drill-in interaction with a Back action.
- Choose presentation explicitly in product code. A compact, coarse-pointer, hover-free media query is one useful policy, but DropdownMenu does not impose a universal device breakpoint.
- When the content is no longer a short list of immediate actions, reevaluate the interaction and choose a component that matches the actual task; content traits alone do not determine the component.
- 避免：Use a DropdownMenu for navigation; use a navigation component instead.
- 避免：Place more than 10–12 items in a single menu without grouping them into sections.

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-dropdown-menu",
      "visualProps": [
        "presentation"
      ]
    },
    {
      "className": "astryx-dropdown-menu-item",
      "visualProps": [
        "size",
        "variant"
      ]
    },
    {
      "className": "astryx-dropdown-menu-radio",
      "visualProps": [
        "size"
      ],
      "states": [
        "checked",
        "disabled"
      ]
    },
    {
      "className": "astryx-dropdown-menu-section-heading"
    },
    {
      "className": "astryx-dropdown-menu-divider"
    },
    {
      "className": "astryx-dropdown-menu-indicator-icon"
    }
  ],
  "vars": [
    {
      "name": "--_dropdown-menu-radius",
      "description": "Border radius of the menu popup",
      "default": "var(--radius-element)",
      "private": true
    },
    {
      "name": "--_dropdown-menu-padding",
      "description": "Inner padding of the menu popup",
      "default": "var(--spacing-1)",
      "private": true
    }
  ],
  "derived": [
    {
      "property": "borderRadius",
      "vars": [
        "--_dropdown-menu-radius"
      ]
    },
    {
      "property": "padding",
      "vars": [
        "--_dropdown-menu-padding"
      ]
    }
  ]
}
```

## DropdownMenuItem



### 组件结构

- **Trigger button**: Button that opens and closes the selected menu presentation.
- **Trigger indicator icon**: Optional trailing chevron shown by a labeled trigger when hasChevron is enabled.
- **Pointer menu surface**: Anchored top-level or nested menu panel used by the pointer presentation.
- **Pointer action row**: Action, selectable option, or submenu trigger row in an anchored menu.
- **Icon-rendered item icon**: Optional semantic or component icon rendered through Icon at the start of an action row.
- **Caller-rendered item start content**: Optional arbitrary React content rendered directly at the start of an action row.
- **Checkbox indicator**: Decorative shared checkbox indicator for a checkbox action row.
- **Radio indicator**: Decorative shared radio indicator with an additional menu-owned target.
- **Pointer section heading**: Heading that labels a group of rows in an anchored menu: a data-driven section or a compound DropdownMenuGroup.
- **Pointer divider**: Divider between groups in an anchored menu.
- **Pointer submenu indicator icon**: Trailing chevron that identifies an action row as a nested flyout trigger.
- **Touch sheet frame**: BottomSheet panel, content area, handle, and optional scrim that host touch actions.
- **Touch menu surface**: Menu-owned content panel rendered inside the touch sheet frame.
- **Touch heading**: Current action-sheet title, updated when a nested action view is opened.
- **Touch action list**: Spacious List that groups actions in the touch presentation.
- **Touch action row**: ListItem button used for an action or drill-in entry in the touch presentation.
- **Touch divider**: Divider between action groups in the touch presentation.

## Dropdown Menu Checkbox Item

An independently checkable option inside DropdownMenu. The row owns menuitemcheckbox semantics while the shared Indicator supplies decorative checkbox paint.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label | ReactNode |  | 标识该项的主标签文本。 |
| description | ReactNode |  | 显示在标签下方的次要描述文本。 |
| icon | IconType |  | 显示在标签前的图标。 |
| value | boolean |  | 该项是否被勾选。受控，与 onChange 搭配使用。 |
| onChange | (checked: boolean) => void |  | 切换该项时以下一个勾选状态触发的回调。 |
| isDisabled | boolean | false | 该项是否禁用。 |
| hasCloseOnSelect | boolean | false | 切换该项是否关闭菜单。默认保持打开。 |
| ref | React.Ref<HTMLElement> |  | 转发到行根元素（带 role="menuitemcheckbox" 的元素）的 ref。 |
| endContent | ReactNode |  | 在标签和描述之后渲染的内容。 |

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-dropdown-menu-item",
      "visualProps": [
        "size"
      ]
    }
  ]
}
```

### 组件结构

- **Checkable row**: The menuitemcheckbox root that owns the accessible name, checked state, focus, and activation.
- **Label/content**: Caller content laid out by Item: the required label plus optional description, icon, and end content.
- **Checkbox marker**: Decorative shared Indicator chrome that visualizes checked and disabled state without adding a second control.

## Dropdown Menu Divider

A horizontal rule separating groups of rows in a compound menu. Renders role="separator", so it is never a stop in the arrow-key order. The data-driven equivalent is the `{type: "divider"}` entry in `items`; both modes render this component, so they look and theme identically.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| xstyle | StyleXStyles |  | StyleX styles applied after the menu spacing. Must be a stylex.create() value: not an inline style object like style={{}}. |

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-dropdown-menu-divider"
    }
  ]
}
```

## Dropdown Menu Group

A titled group of rows in a compound menu. Renders role="group" named by its heading; the heading is plain text, not a menuitem, so arrow keys and typeahead skip it. The data-driven equivalent is the `{type: "section", title, items}` entry in `items`; the heading shares its typography and theme target.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| title | ReactNode |  | Heading shown above the rows and used as the group's accessible name. Omit for an unnamed group. |
| children | ReactNode |  | The rows of the group: DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuSubMenu. |
| xstyle | StyleXStyles |  | StyleX styles applied to the group wrapper. Must be a stylex.create() value: not an inline style object like style={{}}. |

### 主题 API

```json
{
  "targets": [
    {
      "className": "astryx-dropdown-menu-section-heading"
    }
  ]
}
```

## Dropdown Menu Item

用于自定义项渲染的辅助组件，提供一致的样式。宿主属性和 DOM 事件处理器（aria-*、data-*、id、拖放）会落在行根元素上，即带 role="menuitem" 的元素。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| icon | IconType |  | 显示在标签前的图标。 |
| label | ReactNode |  | 主标签文本。 |
| description | ReactNode |  | 显示在标签下方的次要描述文本。 |
| onClick | (event: MouseEvent) => void |  | 选中该项时的回调。键盘激活会以携带修饰键的合成点击到达；带 href 的行会在浏览器导航前运行，带修饰键的点击则交给浏览器处理。 |
| href | string |  | 该行导航到的地址。此时该行渲染为带 role="menuitem" 的真实链接，带修饰键的点击或中键点击保留浏览器语义（新标签页）。 |
| target | '_blank' \| '_self' |  | 链接目标。仅与 href 一起使用。 |
| rel | string |  | 链接关系。target="_blank" 时自动加入 noopener noreferrer。 |
| endContent | ReactNode |  | 在标签和描述之后渲染的附加内容。 |
| hasCloseOnSelect | boolean | true | 激活该项时是否关闭菜单。若操作要在该项上就地反馈结果，请设为 false。 |
| variant | 'default' \| 'destructive' | 'default' | 视觉变体。'destructive' 会以错误色渲染标签、描述和图标，用于危险操作（如删除）。 |
| xstyle | StyleXStyles |  | 根容器的 StyleX 样式。 |
| ref | React.Ref<HTMLElement> |  | 转发到行根元素（带 role="menuitem" 的元素）的 ref。 |

## Dropdown Menu Radio Group

单选的单选菜单项组（menuitemradio 的 role="group"）。持有所选值，并以菜单的项间距布局其子项。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| value | string \| undefined |  | 组中当前选中的值。尚无选中项时传 undefined。 |
| onChange | (value: string) => void |  | 所选值变化时触发的回调。 |
| label | string |  | 组的无障碍名称，作为 aria-label 应用，以便屏幕阅读器将单选项作为命名集合朗读，例如“Sort by”。必需。当名称已作为可见元素存在时，可改用 aria-labelledby（通过基础属性）。 |
| hasCloseOnSelect | boolean | true | 选择某值是否关闭菜单。默认关闭。 |
| children | ReactNode |  | 组成该组的 DropdownMenuRadioItem。 |

## Dropdown Menu Radio Item

DropdownMenuRadioGroup 中的单个选项（role="menuitemradio"）。必须在 DropdownMenuRadioGroup 内使用。

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| value | string |  | 该项在组内代表的值。组的 value 与之匹配以确定勾选状态。 |
| label | ReactNode |  | 标识该选项的主标签文本。 |
| description | ReactNode |  | 显示在标签下方的次要描述文本。 |
| icon | IconType |  | 显示在标签前的图标。 |
| isDisabled | boolean | false | 该单选项是否禁用。 |
| endContent | ReactNode |  | 在标签和描述之后渲染的内容。 |
| ref | React.Ref<HTMLElement> |  | 转发到行根元素（带 role="menuitemradio" 的元素）的 ref。 |

## Dropdown Menu Submenu

A single menu row that reveals a nested flyout of its own items. The row adopts DropdownMenuItem semantics (label / icon / description / isDisabled); its children become the flyout content. Opens inline-end with viewport auto-flip; Right/Enter/Space opens and focuses the first item, Left/Escape closes and returns focus to the trigger (Right/Left swap in RTL). On a phone (a coarse pointer, decided when the menu opened) the row drills in instead: its rows replace the menu's rows in the same box, led by a Back row named "Back to <parent>", and Back, Escape or ArrowLeft return to the row. Host attributes and DOM event handlers (aria-*, data-*, id) reach the trigger row, the element carrying role="menuitem" and aria-haspopup="menu"; a caller's handlers run after the row's own, except for the keys that open the flyout (the open arrow, Enter, Space) and a click on a disabled row. For data-driven menus, give a menu item a nested `items` array instead of using this component directly.

| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label | ReactNode |  | Primary label text for the trigger row. |
| icon | IconType |  | Icon to display before the label. See `astryx docs icons` for valid semantic names. |
| description | ReactNode |  | Secondary description text displayed below the label. |
| children | ReactNode |  | The flyout menu items: the same components used at the top level (DropdownMenuItem, nested DropdownMenuSubMenu, selectable items). |
| isDisabled | boolean | false | A disabled submenu renders its trigger row but never opens the flyout. |
| hasSpinner | boolean | false | Show a spinner in place of the caret, e.g. while a lazy submenu's children are loading. |
| menuWidth | number \| string |  | Minimum flyout width. The flyout may grow for its content, but it is capped to the available viewport space. Defaults to intrinsic sizing (min 160px). |
| onOpenChange | (isOpen: boolean) => void |  | Called when the flyout opens or closes. |
| presentation | 'flyout' \| 'drill-in' \| 'adaptive' | 'adaptive' | How the sub-menu shows its rows. 'flyout' opens them beside the row; 'drill-in' replaces the menu's rows with them and a Back row, in the same box; 'adaptive' drills in when a finger opened the menu (a coarse pointer, decided when the menu opened) and flies out otherwise. |
| xstyle | StyleXStyles |  | StyleX styles for the trigger row. Must be a stylex.create() value: not an inline style object like style={{}}. |
| ref | React.Ref<HTMLElement> |  | Ref forwarded to the trigger row, the element carrying role="menuitem" and aria-haspopup="menu". |


## 完整示例的额外依赖

部分示例使用独立图标包。示例清单中的每条安装命令包含该示例实际需要的依赖；若要运行本页全部示例，还需安装：

```bash
npm install @heroicons/react@2.2.0
```

---

JGuang 全部资源索引: https://jiguang132.com/llms.txt
