<script setup lang="ts">
import { BookOpen, Gamepad2, House, Library } from '@lucide/vue'
import { NavLink, Stack } from '@hina-ui/vue'
const items = [
{ icon: House, label: '首页', active: false },
{ icon: Gamepad2, label: 'Galgame', active: true },
{ icon: BookOpen, label: '轻小说', active: false },
{ icon: Library, label: '漫画', active: false },
]
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink v-for="item in items" :key="item.label" href="#" :active="item.active">
<template #icon><component :is="item.icon" /></template>
{{ item.label }}
</NavLink>
</Stack>
</template>
用法
import { NavLink } from '@hina-ui/vue'
NavLink 是纵向导航列表中的一项,默认渲染为链接,整行都是点击区域。它自己不提供列表容器,外层用 Stack 或 nav 排布。文字只占一行,放不下时以省略号截断。
<script setup lang="ts">
import { NavLink, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink href="#">概览</NavLink>
<NavLink href="#">我的书架</NavLink>
<NavLink href="#">收藏</NavLink>
</Stack>
</template>
它与 Link 的分工是明确的:Link 是正文行内的文字链接,随文字流动;NavLink 是导航结构中的一行,有固定行高与整行热区。正文里不要用 NavLink。
示例
当前项
active 标出读者当前所在的页面:字色回到正色并加重,背景同时浮现一层选中态底色。它还会给元素加上 aria-current="page",屏幕阅读器据此播报当前位置。
一组导航中同时只应有一项为 active。
<script setup lang="ts">
import { NavLink, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink href="#">概览</NavLink>
<NavLink href="#" active>我的书架</NavLink>
<NavLink href="#">收藏</NavLink>
</Stack>
</template>
悬停于选中项时,悬停底色叠加在选中态底色之上,而非替换它,对比度只升不降,因此选中状态在悬停过程中不会丢失。
图标
icon 插槽放在文字之前,尺寸由组件统一为 4 个单位,不需要在图标上写尺寸类。图标是装饰,文字才是可读名称。
<script setup lang="ts">
import { CircleUser, House, Settings } from '@lucide/vue'
import { NavLink, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink href="#" active>
<template #icon><House /></template>
概览
</NavLink>
<NavLink href="#">
<template #icon><CircleUser /></template>
账号
</NavLink>
<NavLink href="#">
<template #icon><Settings /></template>
设置
</NavLink>
</Stack>
</template>
尾部内容
trailing 插槽放在文字之后并贴在行末,用于页数、计数或快捷键。标题放不下时只截断标题,尾部内容保持完整;侧栏收起为 rail 时与文字一起隐藏。
<script setup lang="ts">
import { NavLink, Stack, Text } from '@hina-ui/vue'
const chapters = [
{ title: '序章', pages: '3 页' },
{ title: '第一章 雨停之后,站台上只剩下我们两个人', pages: '28 页', active: true },
{ title: '第二章 夏祭', pages: '19 页' },
{ title: '终章', pages: '6 页' },
]
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink v-for="chapter in chapters" :key="chapter.title" href="#" :active="chapter.active">
{{ chapter.title }}
<template #trailing>
<Text as="span" size="sm" tone="faint" class="tabular-nums">{{ chapter.pages }}</Text>
</template>
</NavLink>
</Stack>
</template>
路由链接
as 指定渲染为何种元素,默认为 a。传入路由组件即可获得客户端跳转,to 等属性会透传过去。当前项由调用方根据路由判断后传入 active,组件不自行匹配路由。
<script setup lang="ts">
import { NuxtLink } from '#components'
import { NavLink, Stack } from '@hina-ui/vue'
const route = useRoute()
const links = [
{ to: '/components/breadcrumb', label: 'Breadcrumb' },
{ to: '/components/nav-link', label: 'NavLink' },
{ to: '/components/splitter', label: 'Splitter' },
]
const localePath = useLocalePath()
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink
v-for="link in links"
:key="link.to"
:as="NuxtLink"
:to="localePath(link.to)"
:active="route.path === localePath(link.to)"
>
{{ link.label }}
</NavLink>
</Stack>
</template>
不可用
disabled 用于暂时不可进入的入口:该项降到半透明、不接受鼠标点击、退出键盘 tab 序列,并带上 aria-disabled="true"。
导航项长期不可用时应当直接移除,而不是保留一个不可用的入口。
<script setup lang="ts">
import { NavLink, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="none" as="nav" class="w-full max-w-56">
<NavLink href="#" active>我的书架</NavLink>
<NavLink href="#">收藏</NavLink>
<NavLink href="#" disabled>创作中心</NavLink>
</Stack>
</template>
收起态
NavLink 放进 Sidebar 后会感知侧栏状态。侧栏收成 rail(只剩图标)时,文字淡出,label 的内容改由悬停提示给出,同时作为该项的无障碍名。
因此放进 Sidebar 的 NavLink 都应当写 label,否则收起后这一项既没有可见文字,也没有可读名称。脱离 Sidebar 时 label 不起作用,也不会渲染提示。用法见 Sidebar 的文档。
收起时导航文字较早淡出,展开时延后淡入。自定义页眉和页脚文字可用 SidebarLabel 保持相同过渡。
无障碍
- 当前项带
aria-current="page",位置信息不依赖颜色。 - 不可用项带
aria-disabled="true"并退出 tab 序列。 - 图标由组件标为装饰,不进入朗读序列;可读名称来自默认插槽的文字。
- 收起成 rail 时,文字虽然不可见,但
label会作为aria-label保留,朗读不受影响。 - 组件本身不渲染
nav地标,外层容器需要自行提供。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
active | boolean | false | 是否为当前页面 |
disabled | boolean | false | 是否不可用 |
label | string | — | 侧栏收起为 rail 时的提示文字与无障碍名 |
as | string | Component | 'a' | 渲染成的元素或组件 |
asChild | boolean | false | 由插槽根元素承担渲染 |
class | string | — | 追加到根元素的类 |
其余属性透传到实际渲染的元素上,例如 href 或路由组件的 to。
Slots
| 插槽 | 说明 |
|---|---|
default | 项的文字,也是可读名称 |
icon | 文字之前的图标 |
trailing | 文字之后、贴在行末的内容 |