# 下拉菜单子菜单

## 使用方法

`DropdownMenuSubMenu` 是菜单里的一行，点开会从旁边飞出下一级。这一行本身的用法和 `DropdownMenuItem` 一样（`label` / `icon` / `description` / `isDisabled`），插槽里的内容就是飞出的那一层。面板默认向行尾那一侧飞出，空间不够时会自动翻到另一侧；右方向键、回车和空格打开并把焦点放到第一项，左方向键和 Escape 关掉它并把焦点还给这一行（从右往左书写的界面里左右互换）。在手机上（菜单打开时就按粗指针判定），这一行不飞出，而是在同一个框里用下一级的行换掉当前的行，最上面多一行返回，文案是「返回 \<父级名字\>」；返回、Escape 或左方向键回到这一行。写在组件上的原生属性和 DOM 事件（`aria-`、`data-`、`id`）落到触发这一行的元素上，也就是带 `role="menuitem"` 和 `aria-haspopup="menu"` 的元素；调用方的处理函数在行自己的逻辑之后运行，只有打开飞出面板的那几个键（打开方向键、回车、空格）和点击禁用行除外。用数据数组写菜单时，直接在菜单项上放嵌套的 `items`，不要再用这个组件。

```ts
import { DropdownMenuSubMenu } from '@astryx-vue/core/DropdownMenu'
```

```vue
<script setup lang="ts">
import { DropdownMenu, DropdownMenuItem, DropdownMenuSubMenu } from '@astryx-vue/core/DropdownMenu'
</script>

<template>
  <DropdownMenu :button="{ label: '操作' }">
    <DropdownMenuItem label="重命名" />
    <DropdownMenuSubMenu label="移动到">
      <DropdownMenuItem label="项目" />
      <DropdownMenuItem label="归档" />
      <DropdownMenuItem label="废纸篓" />
    </DropdownMenuSubMenu>
  </DropdownMenu>
</template>
```

## DropdownMenuSubMenu 属性

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `label` | `string \| number` | — | 这一行的文字，也是下一级返回按钮里提到的父级名字。 |
| `description` | `string \| number` | — | 标签下方的次要说明。 |
| `icon` | `string \| Component` | — | 标签前的图标。 |
| `isDisabled` | `boolean` | `false` | 禁用后这一行不能打开下一级。 |
| `hasSpinner` | `boolean` | `false` | 用转圈代替行尾箭头，表示下一级还在加载。转圈是装饰，不阻止打开。 |
| `menuWidth` | `number \| string` | — | 飞出面板的宽度。数字按像素处理，作为最小宽度并限制在视口内。固有尺寸关键字则作为首选宽度。钻入时不使用。 |
| `presentation` | `'flyout' \| 'drill-in' \| 'adaptive'` | `'adaptive'` | 下一级怎么出现。旁边飞出、在原菜单里替换当前行，或在菜单打开时按是否为紧凑触控界面决定。 |

## 事件

| 名称 | 载荷 | 说明 |
| --- | --- | --- |
| `openChange` | `boolean` | 飞出面板或钻入的视图打开、关闭时触发。 |

## 插槽

- `default`：下一级的行，可以放动作行、勾选行、单选组，以及更深一层的子菜单。
- `icon`：任意前置内容，替代 `icon` 属性。
- `label`：替代 `label` 属性的富文本。
- `description`：替代 `description` 属性的富文本。

## 行的元素

这一行就是带 `role="menuitem"` 和 `aria-haspopup="menu"` 的那个元素。它自己不会跳转：子菜单行永远是打开下一级，所以地址要写在下级那些行上。
