Skip to content

NavigationRail 侧边导航栏 ​

竖向导航。NavigationRail 持有选中值(v-model:value), NavigationRailItem 描述每一项。同一应用里导航栏应当是唯一的导航元素,不要再叠加 NavigationBar / NavigationDrawer。

变体宽度文字展开
variant="static"(默认)固定 80px常显在图标下方不可展开
variant="expandable"80px ↔ 256px折叠时隐藏,展开时出现在图标右侧可由菜单按钮或外部状态控制

子项约定 ​

NavigationRail / NavigationRailItem 遵循子项组件统一范式:选中值由 v-model:value 持有,NavigationRailItem 用 label、icon 或默认插槽描述内容,标识取 value 或 父组件注入的索引。

静态导航栏 ​

图标在上、文字在下,宽度固定 80px,v-model:expanded 与 overlay 都不起作用。适合导航项固定、 不需要展开态的应用(平板 / 桌面常驻侧栏)。

应用内容

当前选中值:timer

vue
<script setup lang="ts">
import { ref } from "vue";
import { NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import alarm from "@iconify-icons/material-symbols/alarm";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import schedule from "@iconify-icons/material-symbols/schedule";
import timer from "@iconify-icons/material-symbols/timer";

const current = ref("timer");
</script>

<template>
  <NavigationRail v-model:value="current" label="时钟">
    <NavigationRailItem label="计时器" value="timer" :icon="hourglass" />
    <NavigationRailItem label="闹钟" value="alarm" :icon="alarm" />
    <NavigationRailItem label="时钟" value="clock" :icon="schedule" />
    <NavigationRailItem label="秒表" value="stopwatch" :icon="timer" />
  </NavigationRail>
</template>

需要菜单按钮时传入 menu-icon:点击不会改变宽度,只触发 menu 事件,由使用方决定打开什么 (例如一个 modal drawer)。

可展开导航栏 ​

variant="expandable" 折叠时只渲染图标;展开到 256px 时,文字出现在图标右侧。菜单按钮与 item 图标 始终固定在左侧 80px 内,只有文字区域出现或消失。

展开有三种用法,按需要选择:

模式配置折叠时展开时
挤开内容variant="expandable"80px,仅图标256px,内容向右让位
覆盖内容variant="expandable" overlay80px,仅图标256px,覆盖同一容器内的内容
仅留一个开关overlay + 折叠时 v-if不渲染,只留外部按钮从容器左侧覆盖出现

overlay 用 absolute 锚定最近的定位容器,父元素需要 relative(Tailwind 的 relative 类)。

挤开内容 ​

应用内容

当前选中值:timer,展开:false

vue
<script setup lang="ts">
import { ref } from "vue";
import { NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import alarm from "@iconify-icons/material-symbols/alarm";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import menu from "@iconify-icons/material-symbols/menu";
import menuOpen from "@iconify-icons/material-symbols/menu-open";

const current = ref("timer");
const expanded = ref(false);
</script>

<template>
  <NavigationRail
    v-model:value="current"
    v-model:expanded="expanded"
    variant="expandable"
    :menu-icon="menu"
    :menu-expanded-icon="menuOpen"
  >
    <NavigationRailItem label="计时器" value="timer" :icon="hourglass" />
    <NavigationRailItem label="闹钟" value="alarm" :icon="alarm" />
  </NavigationRail>
</template>

覆盖内容 ​

加 overlay 后展开态不再挤开内容,而是在容器内覆盖在内容上方。它没有遮罩、不锁定页面滚动、 不接管焦点,也不是 modal:点击外部不会自动收起,收起由菜单按钮或使用方控制 v-model:expanded 完成。

应用内容(展开时被 rail 覆盖一部分)

当前选中值:timer,展开:false

vue
<script setup lang="ts">
import { ref } from "vue";
import { NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import alarm from "@iconify-icons/material-symbols/alarm";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import menu from "@iconify-icons/material-symbols/menu";
import menuOpen from "@iconify-icons/material-symbols/menu-open";

const current = ref("timer");
const expanded = ref(false);
</script>

<template>
  <div class="relative flex h-[24rem]">
    <NavigationRail
      v-model:value="current"
      v-model:expanded="expanded"
      variant="expandable"
      overlay
      :menu-icon="menu"
      :menu-expanded-icon="menuOpen"
    >
      <NavigationRailItem label="计时器" value="timer" :icon="hourglass" />
      <NavigationRailItem label="闹钟" value="alarm" :icon="alarm" />
    </NavigationRail>
    <div class="flex-1">应用内容</div>
  </div>
</template>

仅留一个开关 ​

折叠时完全不渲染 rail,只在容器内保留一个菜单按钮;点击后把 v-model:expanded 置为 true,rail 以 覆盖方式出现。展开后仍可用 rail 自带的菜单按钮收起,所以这里保留内置按钮、不用 :menu="false"。

应用内容
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import alarm from "@iconify-icons/material-symbols/alarm";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import menu from "@iconify-icons/material-symbols/menu";
import menuOpen from "@iconify-icons/material-symbols/menu-open";

const open = ref(false);
const current = ref("timer");
</script>

<template>
  <div class="relative flex h-[24rem]">
    <div class="flex-1 p-6">
      <Button @click="open = true">展开导航</Button>
    </div>

    <NavigationRail
      v-if="open"
      v-model:value="current"
      v-model:expanded="open"
      variant="expandable"
      overlay
      :menu-icon="menu"
      :menu-expanded-icon="menuOpen"
      menu-label="收起导航"
    >
      <NavigationRailItem label="计时器" value="timer" :icon="hourglass" />
      <NavigationRailItem label="闹钟" value="alarm" :icon="alarm" />
    </NavigationRail>
  </div>
</template>

内置菜单按钮 ​

行为staticexpandable
点击效果触发 menu 事件,宽度不变切换 v-model:expanded
aria-expanded不输出输出当前展开状态
图标切换始终用 menu-icon展开时优先用 menu-expanded-icon
  • menu(默认 true):是否渲染按钮;只有提供 menu-icon 或 menu 插槽时才真正出现。
  • menu-label:按钮的 aria-label。
  • menu 是作用域插槽,参数为 { expanded },可替换成任意图标或自定义按钮内容:
vue
<script setup lang="ts">
import { ref } from "vue";
import { Icon, NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import menu from "@iconify-icons/material-symbols/menu";
import menuOpen from "@iconify-icons/material-symbols/menu-open";

const expanded = ref(false);
</script>

<template>
  <NavigationRail v-model:expanded="expanded" variant="expandable">
    <template #menu="{ expanded }">
      <Icon :icon="expanded ? menuOpen : menu" size="24" />
    </template>
    <NavigationRailItem label="计时器" :icon="hourglass" />
  </NavigationRail>
</template>

选中值与索引 ​

NavigationRailItem 省略 value 时用自己的数字索引(从 0 开始)作为标识;v-model:value 默认是 0, 所以首项默认选中。

应用内容

当前选中索引:0

vue
<script setup lang="ts">
import { ref } from "vue";
import { NavigationRail, NavigationRailItem } from "@sharpdotnut/dotsharp";
import alarm from "@iconify-icons/material-symbols/alarm";
import hourglass from "@iconify-icons/material-symbols/hourglass";
import schedule from "@iconify-icons/material-symbols/schedule";

const index = ref(0);
</script>

<template>
  <NavigationRail v-model:value="index">
    <NavigationRailItem label="第一项" :icon="hourglass" />
    <NavigationRailItem label="第二项" :icon="alarm" />
    <NavigationRailItem label="第三项" :icon="schedule" />
  </NavigationRail>
</template>

索引由 NavigationRail 按最终渲染顺序注入,规则见统一范式。

单项内容 ​

  • label:纯文本。static 常显在图标下方;expandable 折叠时隐藏,展开时显示在图标右侧。
  • icon:与 Icon 的 icon 同源,尺寸固定 24px。
  • 默认插槽:优先于 label,适合自定义排版;过长文字会截断为省略号。
vue
<NavigationRailItem label="计时器" :icon="hourglass" value="timer" />
<NavigationRailItem value="custom" :icon="schedule">
  <span class="font-medium">自定义内容</span>
</NavigationRailItem>

API ​

属性类型默认值说明
variant"static" | "expandable""static"static 常显文字、不可展开;expandable 可展开
v-model:valuestring | number0当前选中值,对应子项的 value 或索引
v-model:expandedbooleanfalse仅 expandable:是否展开为 256px
overlaybooleanfalse仅 expandable:展开时在定位容器内覆盖内容
menubooleantrue是否渲染内置菜单按钮
menu-labelstring"Toggle navigation"菜单按钮的 aria-label
menu-iconIconifyIcon | string—折叠态菜单图标;没有图标且没有 menu 插槽时按钮不渲染
menu-expanded-iconIconifyIcon | string—展开态菜单图标,省略时沿用 menu-icon
labelstring—无障碍名称,写入 <nav> 的 aria-label

事件:

事件触发时机
menustatic 变体点击内置菜单按钮时;不改变 rail 宽度

插槽:

插槽作用域说明
default—一组 NavigationRailItem
menu{ expanded: boolean }替换内置菜单按钮的内容
属性类型默认值说明
labelstring—纯文本内容;存在默认插槽时被插槽覆盖
iconIconifyIcon | string—图标,尺寸 24px,渲染在文字/插槽之前
valuestring | number所在位置索引子项标识
  • 根元素是 type="button" 的 button,选中项带 aria-current="page"。
  • 默认插槽优先于 label;与 icon 同时使用时插槽内容排在图标之后。
  • 选中指示器:static 与折叠态只包住图标;展开态是整行胶囊。

可访问性 ​

  • 根元素是 <nav>;内置菜单按钮请通过 menu-label 提供可读名称。
  • expandable 的菜单按钮带 aria-expanded;static 按钮没有该属性,只触发 menu 事件。
  • 展开/收起只改变宽度与文字可见性,同一组 button 保留,焦点不会丢失。
  • overlay 只是容器内的绝对定位布局,不产生遮罩、不锁定滚动、不接管焦点,也没有对话框语义。
  • 选中项用 aria-current="page" 表达当前位置,不要只依赖颜色。