import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
用法
import { Menu } from "@chakra-ui/react"
<Menu.Root>
<Menu.Trigger />
<Menu.Positioner>
<Menu.Content>
<Menu.Item />
<Menu.ItemGroup>
<Menu.Item />
</Menu.ItemGroup>
<Menu.Separator />
<Menu.Arrow />
<Menu.CheckboxItem>
<Menu.ItemIndicator />
</Menu.CheckboxItem>
<Menu.RadioItemGroup>
<Menu.RadioItem>
<Menu.ItemIndicator />
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
示例
命令
使用 Menu.ItemCommand
组件在菜单中显示命令。
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt-a">
New Text File <Menu.ItemCommand>⌘E</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="new-file-a">
New File... <Menu.ItemCommand>⌘N</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="new-win-a">
New Window <Menu.ItemCommand>⌘W</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="open-file-a">
Open File... <Menu.ItemCommand>⌘O</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="export-a">
Export <Menu.ItemCommand>⌘S</Menu.ItemCommand>
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
上下文菜单
使用 Menu.ContextTrigger
组件创建上下文菜单。
import { Center, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.ContextTrigger width="full">
<Center
height="40"
userSelect="none"
borderWidth="2px"
borderStyle="dashed"
rounded="lg"
padding="4"
>
Right click here
</Center>
</Menu.ContextTrigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
分组
使用 Menu.ItemGroup
组件对相关菜单项进行分组。
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Styles</Menu.ItemGroupLabel>
<Menu.Item value="bold">Bold</Menu.Item>
<Menu.Item value="underline">Underline</Menu.Item>
</Menu.ItemGroup>
<Menu.Separator />
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Align</Menu.ItemGroupLabel>
<Menu.Item value="left">Left</Menu.Item>
<Menu.Item value="middle">Middle</Menu.Item>
<Menu.Item value="right">Right</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
危险项
这是一个如何为用于删除项目的菜单项设置样式的示例。
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open Menu
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="rename">Rename</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
<Menu.Item
value="delete"
color="fg.error"
_hover={{ bg: "bg.error", color: "fg.error" }}
>
Delete...
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
子菜单
这是一个如何创建子菜单的示例。
import { Button, Menu, Portal } from "@chakra-ui/react"
import { LuChevronRight } from "react-icons/lu"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Root positioning={{ placement: "right-start", gutter: 2 }}>
<Menu.TriggerItem>
Open Recent <LuChevronRight />
</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="ark">Ark UI</Menu.Item>
<Menu.Item value="chakra">Chakra v3</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
链接
将 asChild
prop 传递给 Menu.Item
组件以渲染链接。
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button size="sm" variant="outline">
Select Anime
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
{links.map((link) => (
<Menu.Item key={link.href} asChild value={link.title}>
<a href={link.href} target="_blank" rel="noreferrer">
{link.title}
</a>
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const links = [
{
title: "Naruto",
href: "https://www.crunchyroll.com/naruto",
},
{
title: "One Piece",
href: "https://www.crunchyroll.com/one-piece",
},
{
title: "Attack on Titan",
href: "https://www.crunchyroll.com/attack-on-titan",
},
]
当使用自定义路由链接时,您需要设置 Menu.Root
组件上的 navigate
prop。
"use client"
import { Menu } from "@chakra-ui/react"
import { useNavigate } from "react-router-dom"
const Demo = () => {
const navigate = useNavigate()
return (
<Menu.Root navigate={({ value, node }) => navigate(`/${value}`)}>
{/* ... */}
</Menu.Root>
)
}
单选项目
这是一个如何创建包含单选项目的菜单的示例。
"use client"
import { Button, Menu, Portal } from "@chakra-ui/react"
import { useState } from "react"
import { HiSortAscending } from "react-icons/hi"
const Demo = () => {
const [value, setValue] = useState("asc")
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiSortAscending /> Sort
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content minW="10rem">
<Menu.RadioItemGroup
value={value}
onValueChange={(e) => setValue(e.value)}
>
{items.map((item) => (
<Menu.RadioItem key={item.value} value={item.value}>
{item.label}
<Menu.ItemIndicator />
</Menu.RadioItem>
))}
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const items = [
{ label: "Ascending", value: "asc" },
{ label: "Descending", value: "desc" },
]
复选框项目
这是一个如何创建包含复选框项目的菜单的示例。
"use client"
import { Button, Menu, Portal, useCheckboxGroup } from "@chakra-ui/react"
import { HiCog } from "react-icons/hi"
const Demo = () => {
const group = useCheckboxGroup({ defaultValue: ["autosave"] })
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiCog /> Features
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Features</Menu.ItemGroupLabel>
{items.map(({ title, value }) => (
<Menu.CheckboxItem
key={value}
value={value}
checked={group.isChecked(value)}
onCheckedChange={() => group.toggleValue(value)}
>
{title}
<Menu.ItemIndicator />
</Menu.CheckboxItem>
))}
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const items = [
{ title: "Autosave", value: "autosave" },
{ title: "Detect Language", value: "detect-language" },
{ title: "Spellcheck", value: "spellcheck" },
]
图标和命令
组合菜单以包含图标和命令。
import { Box, Button, Menu, Portal } from "@chakra-ui/react"
import { LuClipboardPaste, LuCopy, LuScissors } from "react-icons/lu"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="cut">
<LuScissors />
<Box flex="1">Cut</Box>
<Menu.ItemCommand>⌘X</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="copy">
<LuCopy />
<Box flex="1">Copy</Box>
<Menu.ItemCommand>⌘C</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="paste">
<LuClipboardPaste />
<Box flex="1">Paste</Box>
<Menu.ItemCommand>⌘V</Menu.ItemCommand>
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
定位
使用 positioning.placement
prop 来控制菜单的定位。
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root positioning={{ placement: "right-start" }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
锚点
使用 positioning.anchorPoint
prop 来控制菜单的锚点。
您可以从 DOM 元素的 getBoundingClientRect
中获取它,或者使用 DOMRect.fromRect({ x: 0, y: 0, width: 1, height: 1 })
等方法创建一个新的矩形。
"use client"
import { Box, Button, Menu, Portal } from "@chakra-ui/react"
import { useRef } from "react"
const Demo = () => {
const ref = useRef<HTMLDivElement | null>(null)
const getAnchorRect = () => ref.current!.getBoundingClientRect()
return (
<Menu.Root positioning={{ getAnchorRect }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Box layerStyle="fill.subtle" p="4" ref={ref} mt="4">
Anchor
</Box>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
混合布局
这是一个如何创建菜单项混合布局的示例。在此布局中,顶部的水平菜单包含常用菜单项。
import { Box, Button, Group, Menu, Portal } from "@chakra-ui/react"
import {
LuClipboard,
LuCopy,
LuFileSearch,
LuMessageSquare,
LuScissors,
LuShare,
} from "react-icons/lu"
const horizontalMenuItems = [
{ label: "Cut", value: "cut", icon: <LuScissors /> },
{ label: "Copy", value: "copy", icon: <LuCopy /> },
{ label: "Paste", value: "paste", icon: <LuClipboard /> },
]
const verticalMenuItems = [
{ label: "Look Up", value: "look-up", icon: <LuFileSearch /> },
{ label: "Translate", value: "translate", icon: <LuMessageSquare /> },
{ label: "Share", value: "share", icon: <LuShare /> },
]
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Group grow gap="0">
{horizontalMenuItems.map((item) => (
<Menu.Item
key={item.value}
value={item.value}
width="14"
gap="1"
flexDirection="column"
justifyContent="center"
>
{item.icon}
{item.label}
</Menu.Item>
))}
</Group>
{verticalMenuItems.map((item) => (
<Menu.Item key={item.value} value={item.value}>
<Box flex="1">{item.label}</Box>
{item.icon}
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
分离时隐藏
当菜单在可滚动容器中渲染时,将 positioning.hideWhenDetached
设置为 true
,以便当触发器滚动出视图时隐藏菜单。
项0
项1
项2
项3
项4
项5
import { Box, Center, Flex, Menu, Portal, Text } from "@chakra-ui/react"
const Demo = () => {
return (
<Center minH="sm">
<Flex
w="300px"
h="full"
overflowX="auto"
gapX="6"
p="4"
borderWidth="1px"
bg="bg.subtle"
>
{[...Array(6).keys()].map((x) => (
<Box layerStyle="fill.surface" p="4" borderRadius="md" key={x}>
<Text>Item{x}</Text>
</Box>
))}
<Box>
<Menu.Root positioning={{ hideWhenDetached: true }}>
<Menu.Trigger asChild>
<Box as="button" bg="green.100" p="4" borderRadius="md">
Menu
</Box>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
</Box>
</Flex>
</Center>
)
}
在对话框内
若要在 Dialog
中使用 Menu
,您需要避免将 Menu.Positioner
传送到文档的 body。
-<Portal>
<Menu.Positioner>
<Menu.Content>
{/* ... */}
</Menu.Content>
</Menu.Positioner>
-</Portal>
"use client"
import { Button, Dialog, Menu, Portal } from "@chakra-ui/react"
import Lorem from "react-lorem-ipsum"
const Demo = () => {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Welcome to the menu</Dialog.Title>
</Dialog.Header>
<Dialog.Body spaceY="4">
<DialogMenu />
<Lorem p={1} />
</Dialog.Body>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}
const DialogMenu = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Menu
</Button>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
属性
根
属性 | 默认值 | 类型 |
---|---|---|
closeOnSelect | true | boolean 选择选项时是否关闭菜单 |
composite | true | boolean 菜单是否与其他复合小部件(如组合框或选项卡)组合 |
lazyMount | true | boolean 是否启用懒加载 |
loopFocus | false | boolean 是否循环键盘导航。 |
typeahead | true | boolean 按下可打印字符时是否触发预输入导航 |
unmountOnExit | true | boolean 退出时是否卸载。 |
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' 组件的颜色调色板 |
variant | 'subtle' | 'subtle' | 'solid' 组件的变体 |
size | 'md' | 'sm' | 'md' 组件的尺寸 |
anchorPoint | Point 菜单的定位点。可通过上下文菜单触发器或按钮触发器设置。 | |
aria-label | string 菜单的可访问性标签 | |
defaultOpen | boolean 菜单首次渲染时的初始打开状态。当您不需要控制其打开状态时使用。 | |
highlightedValue | string 高亮菜单项的值。 | |
id | string 机器的唯一标识符。 | |
ids | Partial<{ trigger: string contextTrigger: string content: string groupLabel(id: string): string group(id: string): string positioner: string arrow: string }> 菜单中元素的 ID。用于组合时非常有用。 | |
immediate | boolean 是立即同步当前更改还是将其延迟到下一帧 | |
navigate | (details: NavigateDetails) => void 如果所选项目是锚点元素,则导航到该项目的功能 | |
onEscapeKeyDown | (event: KeyboardEvent) => void 按下 Esc 键时调用的函数 | |
onExitComplete | () => void 动画在关闭状态结束时调用的函数 | |
onFocusOutside | (event: FocusOutsideEvent) => void 焦点移出组件时调用的函数 | |
onHighlightChange | (details: HighlightChangeDetails) => void 高亮菜单项更改时调用的函数。 | |
onInteractOutside | (event: InteractOutsideEvent) => void 组件外部发生交互时调用的函数 | |
onOpenChange | (details: OpenChangeDetails) => void 菜单打开或关闭时调用的函数 | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void 指针在组件外部按下时调用的函数 | |
onSelect | (details: SelectionDetails) => void 选中菜单项时调用的函数。 | |
open | boolean 菜单是否打开 | |
positioning | PositioningOptions 用于动态定位菜单的选项 | |
present | boolean 节点是否存在(由用户控制) | |
as | React.ElementType 要渲染的基础元素。 | |
asChild | ||
unstyled | boolean 是否移除组件的样式。 |
项
属性 | 默认值 | 类型 |
---|---|---|
value * | string 菜单项选项的唯一值。 | |
asChild | ||
closeOnSelect | boolean 选中选项时是否应关闭菜单。 | |
disabled | boolean 菜单项是否禁用 | |
valueText | string 选项的文本值。用于菜单的预输入导航。如果未提供,则使用菜单项的文本内容。 |