跳到主要内容
+

菜单

下拉菜单组件为最终用户在临时表面上提供选项列表。

简介

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 组件,使其在视觉上突破其父容器

Enter 开始编辑

<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,并使用过渡组件(CssTransitionCssAnimation 或自定义构建的组件)包裹它。

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 构建下拉菜单

Enter 开始编辑

组件及其相应的 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 组件还可以包含非交互式子组件,例如辅助文本。

以下演示展示了一个下拉菜单的示例,其中项目分组在非交互式标题下,以及显示 当前缩放级别 的辅助文本