# 移动端导航

## 使用方法

`MobileNav` 是从屏幕一侧滑入的对话框抽屉，不是底部弹层。`MobileNavToggle` 和它绑同一个 `v-model:is-open`。打开后焦点留在抽屉里，Escape 或点遮罩关闭，并回到原来的焦点。抽屉里面再开一层浮层时，Escape 先关最上面那一层。双指缩放留给浏览器，内容区仍可以竖着滚。

`side` 可以是 `start`、`end` 或 `auto`。`auto` 按打开时焦点的位置决定从哪一侧进入。顶栏的窄屏模式会自己创建这一对组件。

```vue
<script setup lang="ts">
import { MobileNav, MobileNavToggle } from '@astryx-vue/core/MobileNav'
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <MobileNavToggle v-model:is-open="open" controls="app-nav" />
  <MobileNav id="app-nav" v-model:is-open="open" header="菜单" side="start">
    <!-- 导航项 -->
  </MobileNav>
</template>
```

### 结构

- **导航遮罩**: 铺满视口、承载移动导航抽屉的对话框遮罩。
- **抽屉**: 从解析出的视口边缘滑入的着色面板。
- **页眉**: 固定的一行，装着可选的页眉内容和关闭按钮。
- **内容**: 装着导航内容的可滚动区域。
- **关闭按钮**: 关闭导航抽屉的按钮。
- **开关按钮**: 能配合 AppShell 的 Button，在移动视口上打开或关闭抽屉。

## 最佳实践

| 指引 | 实践 |
| --- | --- |
| 推荐 | 开关的 <code>controls</code> 指向抽屉的 id，读屏能把按钮和对话框连起来。 |
| 推荐 | 抽屉里放普通链接。不要再套一层 <code>role="menu"</code>。 |
| 避免 | 不要用底部弹层来做这套导航。它是侧边抽屉。 |

## 示例

### 基本抽屉

菜单按钮打开分组导航。

```vue
<script setup lang="ts">
import { MobileNav, MobileNavToggle } from '@astryx-vue/core/MobileNav'
import { SideNavItem, SideNavSection } from '@astryx-vue/core/SideNav'
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <div>
    <MobileNavToggle v-model:is-open="open" controls="docs-mobile-nav" label="Open Navigation" />
    <MobileNav id="docs-mobile-nav" v-model:is-open="open" header="Navigation" side="start">
      <SideNavSection title="Main">
        <SideNavItem href="#dashboard" icon-class="i-carbon-home" is-selected label="Dashboard" />
        <SideNavItem href="#projects" icon-class="i-carbon-folder" label="Projects" />
        <SideNavItem href="#analytics" icon-class="i-carbon-analytics" label="Analytics" />
      </SideNavSection>
      <SideNavSection title="Settings">
        <SideNavItem href="#settings" icon-class="i-carbon-settings" label="General" />
        <SideNavItem href="#team" icon-class="i-carbon-group" label="Team" />
      </SideNavSection>
    </MobileNav>
  </div>
</template>
```

### 结束边抽屉

同一套抽屉从结束边滑入。

```vue
<script setup lang="ts">
import { Button } from '@astryx-vue/core/Button'
import { MobileNav } from '@astryx-vue/core/MobileNav'
import { SideNavItem, SideNavSection } from '@astryx-vue/core/SideNav'
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <div>
    <Button label="Open from Right" @click="open = true" />
    <MobileNav id="docs-mobile-nav-end" v-model:is-open="open" header="Settings" side="end">
      <SideNavSection title="Settings">
        <SideNavItem href="#settings" icon-class="i-carbon-settings" label="General" />
        <SideNavItem href="#team" icon-class="i-carbon-group" label="Team" />
      </SideNavSection>
    </MobileNav>
  </div>
</template>
```

### 无标题

不传标题时，抽屉里只剩导航项和关闭按钮。

```vue
<script setup lang="ts">
import { MobileNav, MobileNavToggle } from '@astryx-vue/core/MobileNav'
import { SideNavItem, SideNavSection } from '@astryx-vue/core/SideNav'
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <div>
    <MobileNavToggle v-model:is-open="open" controls="docs-mobile-nav-plain" label="Open Navigation" />
    <MobileNav id="docs-mobile-nav-plain" v-model:is-open="open" side="start">
      <SideNavSection title="Main">
        <SideNavItem href="#dashboard" icon-class="i-carbon-home" is-selected label="Dashboard" />
        <SideNavItem href="#projects" icon-class="i-carbon-folder" label="Projects" />
      </SideNavSection>
    </MobileNav>
  </div>
</template>
```

## MobileNav Props

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `isOpen` | `boolean` | `false` | 打开状态，配合 `v-model:is-open`。 |
| `header` | `string` | - | 抽屉标题。也可以用 `header` 插槽。 |
| `width` | `number` | `320` | 抽屉宽度，单位像素。 |
| `side` | `'start' \| 'end' \| 'auto'` | `'auto'` | 从哪一侧滑入。 |
| `label` | `string` | 标题或当前语言的「导航」 | 没有标题时作为对话框的无障碍名称。 |

## MobileNav 事件

| 名称 | 说明 |
| --- | --- |
| `update:isOpen` | 打开状态变化，用于 `v-model:is-open`。 |
| `openChange` | 打开状态变化时触发。 |

## MobileNavToggle Props

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `isOpen` | `boolean` | `false` | 和抽屉绑同一个 `v-model:is-open`。 |
| `label` | `string` | 打开时「关闭」，关闭时「打开菜单」 | 按钮的无障碍名称。 |
| `controls` | `string` | - | 对应抽屉的 id，写到 `aria-controls`。 |
