导航菜单
功能
- 可受控或非受控。
- 灵活的布局结构,支持管理选项卡焦点。
- 支持子菜单。
- 可选的活动项指示器。
- 完整的键盘导航。
- 暴露 CSS 变量以实现高级动画。
- 支持自定义时间。
安装
从命令行安装此组件。
$ npm add reka-ui结构
导入所有部分并将其组合。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuIndicator,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuSub,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger />
<NavigationMenuContent>
<NavigationMenuLink />
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink />
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger />
<NavigationMenuContent>
<NavigationMenuSub>
<NavigationMenuList />
<NavigationMenuViewport />
</NavigationMenuSub>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuIndicator />
</NavigationMenuList>
<NavigationMenuViewport />
</NavigationMenuRoot>
</template>API 参考
根
包含导航菜单的所有部分。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'nav' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
defaultValue | string菜单项的初始活动值。 当您不需要控制值状态时使用。 | |
delayDuration | 200 | 数字从指针进入触发器到工具提示打开的持续时间。 |
dir | 'ltr' | 'rtl'适用时,组合框的阅读方向。 如果省略,则全局继承自 | |
disableClickTrigger | false | 布尔值如果 |
disableHoverTrigger | false | 布尔值如果 |
disablePointerLeaveClose | 布尔值如果 | |
modelValue | string要激活的菜单项的受控值。可用作 | |
orientation | 'horizontal' | 'vertical' | 'horizontal'菜单的方向。 |
skipDelayDuration | 300 | 数字用户在不再次产生延迟的情况下进入另一个触发器的时间。 |
unmountOnHide | true | 布尔值当 |
| 事件触发 | 载荷 |
|---|---|
update:modelValue | [value: string]值改变时调用的事件处理程序。 |
| 插槽(默认) | 载荷 |
|---|---|
modelValue | string当前输入值 |
| 数据属性 | 值 |
|---|---|
[data-orientation] | "垂直" | "水平" |
子菜单
表示一个子菜单。在嵌套时,用它替代根组件以创建子菜单。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'div' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
defaultValue | string菜单项的初始活动值。 当您不需要控制值状态时使用。 | |
modelValue | string要激活的子菜单项的受控值。可用作 | |
orientation | 'horizontal' | 'vertical' | 'horizontal'菜单的方向。 |
| 事件触发 | 载荷 |
|---|---|
update:modelValue | [value: string]值改变时调用的事件处理程序。 |
| 插槽(默认) | 载荷 |
|---|---|
modelValue | string当前输入值 |
| 数据属性 | 值 |
|---|---|
[data-orientation] | "垂直" | "水平" |
列表
包含顶级菜单项。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'ul' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
| 数据属性 | 值 |
|---|---|
[data-orientation] | "垂直" | "水平" |
菜单项
一个顶级菜单项,包含链接或触发器与内容的组合。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'li' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
value | string当导航菜单受控时,将项与活动值关联的唯一值。 当不受控时,此属性会自动管理。 |
触发器
切换内容的按钮。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'button' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
disabled | 布尔值当为 |
| 数据属性 | 值 |
|---|---|
[data-state] | "打开" | "关闭" |
[data-disabled] | 禁用时存在 |
内容
包含与每个触发器关联的内容。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'div' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
disableOutsidePointerEvents | 布尔值当为 | |
forceMount | 布尔值当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 |
| 事件触发 | 载荷 |
|---|---|
escapeKeyDown | [事件: KeyboardEvent]按下 Esc 键时调用的事件处理程序。可阻止其默认行为。 |
focusOutside | [事件: FocusOutsideEvent]当焦点移出 |
interactOutside | [事件: PointerDownOutsideEvent | FocusOutsideEvent]当 |
pointerDownOutside | [事件: PointerDownOutsideEvent]当 |
| 数据属性 | 值 |
|---|---|
[data-state] | "打开" | "关闭" |
[data-motion] | "to-start" | "to-end" | "from-start" | "from-end" |
[data-orientation] | "垂直" | "水平" |
链接
一个导航链接。
| 属性 | 默认 | 类型 |
|---|---|---|
active | 布尔值用于标识该链接为当前活动页面。 | |
as | 'a' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 |
| 事件触发 | 载荷 |
|---|---|
select | [payload: CustomEvent<{ originalEvent: Event; }>]当用户选择链接(通过鼠标或键盘)时调用的事件处理程序。 在此处理程序中调用 |
| 数据属性 | 值 |
|---|---|
[data-active] | 激活时存在 |
指示器
一个可选的指示器元素,渲染在列表下方,用于突出显示当前活动的触发器。
| 属性 | 默认 | 类型 |
|---|---|---|
as | 'div' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
forceMount | 布尔值当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 |
| 数据属性 | 值 |
|---|---|
[data-state] | "可见" | "隐藏" |
[data-orientation] | "垂直" | "水平" |
| CSS 变量 | 描述 |
|---|---|
--reka-navigation-menu-indicator-size | 指示器的大小。 |
--reka-navigation-menu-indicator-position | 指示器的位置 |
视口
一个可选的视口元素,用于在列表外部渲染活动内容。
| 属性 | 默认 | 类型 |
|---|---|---|
align | 'center' | '开始' | '居中' | '结束'视口相对于 CSS 变量 |
as | 'div' | AsTag | Component此组件应渲染为的元素或组件。可通过 |
asChild | 布尔值更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。 阅读我们的 组合指南了解更多详情。 | |
forceMount | 布尔值当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。 |
| 数据属性 | 值 |
|---|---|
[data-state] | "可见" | "隐藏" |
[data-orientation] | "垂直" | "水平" |
| CSS 变量 | 描述 |
|---|---|
--reka-navigation-menu-viewport-width | 视口可见/隐藏时的宽度,根据活动内容计算。 |
--reka-navigation-menu-viewport-height | 视口可见/隐藏时的高度,根据活动内容计算。 |
示例
垂直方向
您可以使用orientation属性创建垂直菜单。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuIndicator,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuSub,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot orientation="vertical">
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item one</NavigationMenuTrigger>
<NavigationMenuContent>Item one content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item two</NavigationMenuTrigger>
<NavigationMenuContent>Item Two content</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuRoot>
</template>灵活布局
当您需要额外控制Content的渲染位置时,请使用Viewport部分。这在您的设计需要调整 DOM 结构或您需要灵活性以实现高级动画时会很有帮助。选项卡焦点将自动维护。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item one</NavigationMenuTrigger>
<NavigationMenuContent>Item one content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item two</NavigationMenuTrigger>
<NavigationMenuContent>Item two content</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
<!-- NavigationMenuContent will be rendered here when active -->
<NavigationMenuViewport />
</NavigationMenuRoot>
</template>带指示器
您可以使用可选的Indicator部分来突出显示当前活动的Trigger,这在您希望提供动画视觉提示(例如箭头或高亮)以配合Viewport时非常有用。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item one</NavigationMenuTrigger>
<NavigationMenuContent>Item one content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item two</NavigationMenuTrigger>
<NavigationMenuContent>Item two content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuIndicator class="NavigationMenuIndicator" />
</NavigationMenuList>
<NavigationMenuViewport />
</NavigationMenuRoot>
</template>/* styles.css */
.NavigationMenuIndicator {
background-color: grey;
position: absolute;
transition: width, transform, 250ms ease;
}
.NavigationMenuIndicator[data-orientation="horizontal"] {
left: 0;
height: 3px;
transform: translateX(var(--reka-navigation-menu-indicator-position));
width: var(--reka-navigation-menu-indicator-size);
}带子菜单
通过嵌套您的NavigationMenu并使用Sub部分替代其Root来创建子菜单。子菜单与Root导航菜单的工作方式不同,它们类似于Tabs,即始终应有一个项目处于活动状态,因此请务必分配并设置defaultValue。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuSub,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item one</NavigationMenuTrigger>
<NavigationMenuContent>Item one content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item two</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuSub default-value="sub1">
<NavigationMenuList>
<NavigationMenuItem value="sub1">
<NavigationMenuTrigger>Sub item one</NavigationMenuTrigger>
<NavigationMenuContent> Sub item one content </NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem value="sub2">
<NavigationMenuTrigger>Sub item two</NavigationMenuTrigger>
<NavigationMenuContent> Sub item two content </NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuSub>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuRoot>
</template>与客户端路由结合
如果您需要使用路由包提供的RouterLink组件,我们建议在NavigationMenuLink中添加asChild="true",或设置as="RouterLink"。这将确保可访问性和键盘控制的一致性。
<script setup lang="ts">
import { NavigationMenuItem, NavigationMenuList, NavigationMenuRoot } from 'reka-ui'
// RouterLink should be injected by default if using `vue-router`
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink as-child>
<RouterLink to="/">
Home
</RouterLink>
<NavigationMenuLink />
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink
:as="RouterLink"
to="/about"
>
About
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuRoot>
</template>高级动画
我们暴露了--reka-navigation-menu-viewport-[width|height]和data-motion['from-start'|'to-start'|'from-end'|'to-end']属性,以便您可以根据进入/退出方向来动画Viewport大小和Content位置。
将这些与position: absolute;结合使用,可以在项目之间移动时创建平滑的重叠动画效果。
<script setup lang="ts">
import {
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuList,
NavigationMenuRoot,
NavigationMenuTrigger,
NavigationMenuViewport,
} from 'reka-ui'
</script>
<template>
<NavigationMenuRoot>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item one</NavigationMenuTrigger>
<NavigationMenuContent class="NavigationMenuContent">
Item one content
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item two</NavigationMenuTrigger>
<NavigationMenuContent class="NavigationMenuContent">
Item two content
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
<NavigationMenuViewport class="NavigationMenuViewport" />
</NavigationMenuRoot>
</template>/* styles.css */
.NavigationMenuContent {
position: absolute;
top: 0;
left: 0;
animation-duration: 250ms;
animation-timing-function: ease;
}
.NavigationMenuContent[data-motion="from-start"] {
animation-name: enterFromLeft;
}
.NavigationMenuContent[data-motion="from-end"] {
animation-name: enterFromRight;
}
.NavigationMenuContent[data-motion="to-start"] {
animation-name: exitToLeft;
}
.NavigationMenuContent[data-motion="to-end"] {
animation-name: exitToRight;
}
.NavigationMenuViewport {
position: relative;
width: var(--reka-navigation-menu-viewport-width);
height: var(--reka-navigation-menu-viewport-height);
transition: width, height, 250ms ease;
}
@keyframes enterFromRight {
from {
opacity: 0;
transform: translateX(200px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes enterFromLeft {
from {
opacity: 0;
transform: translateX(-200px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes exitToRight {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(200px);
}
}
@keyframes exitToLeft {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-200px);
}
}可访问性
与菜单栏的区别
NavigationMenu不应与menubar混淆,尽管这个基本元素在口语上共享menu的名称以指代一组导航链接,但它不使用 WAI-ARIA menu角色。这是因为menu和menubars的行为类似于桌面应用程序窗口中最常见的原生操作系统菜单,因此它们具有复合焦点管理和首字符导航等复杂功能。
这些功能通常被认为对于网站导航来说是不必要的,最坏的情况是可能会混淆熟悉现有网站模式的用户。
有关更多信息,请参阅 W3C 披露导航菜单示例。
链接用法和aria-current
在菜单中,所有导航链接都必须使用NavigationMenuLink,这不仅适用于主列表,也适用于通过NavigationMenuContent渲染的任何内容。这将确保键盘交互和可访问性的一致性,同时还提供对active属性的访问,用于设置aria-current和活动样式。有关与第三方路由组件一起使用的更多信息,请参阅此示例。
键盘交互
| 键 | 描述 |
|---|---|
SpaceEnter |
当焦点位于 NavigationMenuTrigger上时,打开内容。
|
Tab | 将焦点移动到下一个可聚焦元素。 |
向下箭头键 |
当为 horizontal且焦点位于打开的NavigationMenuTrigger上时,将焦点移入NavigationMenuContent。将焦点移动到下一个 NavigationMenuTrigger或NavigationMenuLink。
|
向上箭头键 |
将焦点移动到上一个 NavigationMenuTrigger或NavigationMenuLink。
|
向右箭头键向左箭头键 |
当为 vertical且焦点位于打开的NavigationMenuTrigger上时,将焦点移入其NavigationMenuContent。将焦点移动到下一个/上一个 NavigationMenuTrigger或NavigationMenuLink。
|
HomeEnd |
将焦点移动到第一个/最后一个 NavigationMenu.Trigger或NavigationMenu.Link。
|
Esc键 |
关闭打开的 NavigationMenu.Content并将焦点移至其NavigationMenu.Trigger。
|
