菜单
下拉菜单组件为最终用户在临时表面上提供选项列表。
简介
MUI Base 下拉菜单是使用相关组件的集合实现的
- Dropdown - 容纳所有菜单组件的最外层容器。
- Menu Button - 切换菜单可见性的按钮。
- Menu - 菜单项的无序列表。
- Menu Item - 菜单的各个列表项。
组件
import { Dropdown } from '@mui/base/Dropdown';
import { MenuButton } from '@mui/base/MenuButton';
import { Menu } from '@mui/base/Menu';
import { MenuItem } from '@mui/base/MenuItem';
下面的演示展示了如何创建和样式化下拉菜单。点击 仪表盘 查看菜单。请注意,它使用了内置的 Popper 组件,使其在视觉上突破其父容器
<Dropdown />
应该是最外层的组件——所有其他与菜单相关的组件都必须作为其子组件放置(但不一定是直接子组件)。如果您需要控制菜单的打开状态或对其更改做出反应,请在 <Dropdown />
上放置 open
/onOpenChange
props。
<Dropdown />
必须仅包含一个 <MenuButton />
和一个 <Menu />
。它会将它们连接在一起,以便按下按钮将打开菜单。它还负责分配适当的可访问性属性,以便下拉菜单可以与辅助技术或键盘一起使用。
<Menu />
托管的 <MenuItem />
组件可以包裹在任意标签和组件中,也可以分组在一起。点击菜单项会关闭其关联的菜单。
解剖
<Dropdown />
不渲染任何 HTML 元素——它仅提供将菜单按钮链接到菜单的 context,因此您不必这样做。<MenuButton />
渲染一个<button>
。<Menu />
组件渲染一个<div>
,其中嵌套了一个<ul>
。<MenuItem />
渲染一个<li>
。
<button class="base-MenuButton-root">Click me</button>
<div class="base-Menu-root">
<ul class="base-Menu-listbox">
<li class="base-MenuItem-root">List item</li>
</ul>
</div>
自定义结构
使用 slots
prop 来覆盖除 <Dropdown />
之外的任何组件上的 slots(因为它不渲染 HTML)
<Menu slots={{ listbox: 'ol' }} />
使用 slotProps
prop 将自定义 props 传递给内部 slots。以下代码片段将名为 my-listbox
的 CSS 类应用于 Menu 上的 listbox slot
<Menu slotProps={{ listbox: { className: 'my-listbox' } }} />
与 TypeScript 一起使用
在 TypeScript 中,您可以将 slots.root
中使用的自定义组件类型指定为非样式化组件的泛型参数。这样,您可以安全地直接在组件上提供自定义 root 的 props
<Menu<typeof CustomComponent> slots={{ root: CustomComponent }} customProp />
这同样适用于特定于自定义原始元素的 props
<Menu<'ol'> slots={{ root: 'ol' }} start={5} />
过渡效果
Menu 组件支持 Transitions API,因此可以动画化 Listbox 的出现和消失。为此,请覆盖 Menu 的 Listbox slot,并使用过渡组件(CssTransition、CssAnimation 或自定义构建的组件)包裹它。
Hooks
import { useDropdown } from '@mui/base/useDropdown';
import { useMenuButton } from '@mui/base/useMenuButton';
import { useMenu } from '@mui/base/useMenu';
import { useMenuItem } from '@mui/base/useMenuItem';
下拉菜单 hooks 让您可以将下拉菜单套件的功能应用于完全自定义的组件。它们返回要放置在自定义组件上的 props,以及表示组件内部状态的字段。
Hooks 不 支持 slot props,但它们支持 自定义 props。
以下演示展示了如何使用 hooks 构建下拉菜单
组件及其相应的 hooks 可以互换使用——例如,您可以创建一个包含使用 useMenuItem
hook 构建的自定义菜单项的 Menu 组件。
性能
useMenuItem
hook 监听由父 Menu 组件设置的 context 中的更改。此 context 在每次项目突出显示时都会更改。通常,这不应该是一个问题,但是,当您的菜单有数百个项目时,您可能会注意到它不是很灵敏,因为每次突出显示更改时,每个项目都会重新渲染。
为了通过防止菜单项不必要地渲染来提高性能,您可以创建一个组件来包裹菜单项。在此组件内部,调用 useMenuItemContextStabilizer
并使用 hook 结果中的值创建 ListContext
const StableMenuItem = React.forwardRef(function StableMenuItem(
props: MenuItemProps,
ref: React.ForwardedRef<Element>,
) {
const { contextValue, id } = useMenuItemContextStabilizer(props.id);
return (
<ListContext.Provider value={contextValue}>
<MenuItem {...props} id={id} ref={ref} />
</ListContext.Provider>
);
});
useMenuItemContextStabilizer
hook 确保 context 值仅在菜单项的状态更新时才更改。
自定义
包裹菜单项
菜单项组件不必是 Menu 组件的直接子组件。您可以将它们包裹在任何需要的组件中,以实现所需的外观。
除了菜单项组件之外,Menu 组件还可以包含非交互式子组件,例如辅助文本。
以下演示展示了一个下拉菜单的示例,其中项目分组在非交互式标题下,以及显示 当前缩放级别 的辅助文本