导航菜单
功能
- 可受控或非受控。
- 灵活的布局结构,支持管理选项卡焦点。
- 支持子菜单。
- 可选的活动项指示器。
- 完整的键盘导航。
- 暴露 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 。
|