【Material-UI】Menu 组件中的 Positioned Menu 和 MenuList Composition 详解

Material-UI 是 React 框架中最流行的 UI 库之一,它提供了众多可复用的组件。Menu 组件是应用中常见的下拉菜单功能,它基于 Popover 组件进行定位,允许用户自定义位置及行为。本文将详细介绍 Menu 组件中的 Positioned Menu 和 MenuList Composition,并展示如何在不同场景下灵活运用这些组件。

一、Positioned Menu 组件概述

1. 什么是 Positioned Menu?

Positioned Menu 是 Material-UI 中基于 Popover 组件实现的菜单,允许用户根据特定的锚点(anchor)来定位菜单。这个功能对于复杂的 UI 布局非常有用,能够在不同的按钮或界面元素上展示菜单,并控制菜单的打开位置。

2. Positioned Menu 的主要功能

通过设置 anchorOrigintransformOrigin 属性,Positioned Menu 允许开发者定义菜单相对于触发按钮的具体位置。这些属性可以分别定义菜单的起点和变换起点,例如将菜单显示在按钮的上方或左侧。

二、Positioned Menu 的基本用法

以下是一个使用 Positioned Menu 的示例:

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';

export default function PositionedMenu() {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const open = Boolean(anchorEl);
  const handleClick = (event) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <div>
      <Button
        id="demo-positioned-button"
        aria-controls={open ? 'demo-positioned-menu' : undefined}
        aria-haspopup="true"
        aria-expanded={open ? 'true' : undefined}
        onClick={handleClick}
      >
        Dashboard
      </Button>
      <Menu
        id="demo-positioned-menu"
        aria-labelledby="demo-positioned-button"
        anchorEl={anchorEl}
        open={open}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
      >
        <MenuItem onClick={handleClose}>Profile</MenuItem>
        <MenuItem onClick={handleClose}>My account</MenuItem>
        <MenuItem onClick={handleClose}>Logout</MenuItem>
      </Menu>
    </div>
  );
}

3. anchorOrigin 和 transformOrigin 的作用

在这个例子中,anchorOrigintransformOrigin 是控制菜单显示位置的两个关键属性:

  • anchorOrigin:定义菜单的起始位置相对于锚点的位置。这里的设置是 topleft,意味着菜单会从锚点的左上角弹出。
  • transformOrigin:定义菜单的内容从哪里开始展开。在示例中,菜单内容从左上角展开。

4. Positioned Menu 的优势

  • 灵活性高:通过配置 anchorOrigintransformOrigin,开发者可以灵活地控制菜单的展开方式和位置。
  • 易于整合:Positioned Menu 基于 Popover 组件,直接集成于现有的 Material-UI 项目中,无需额外复杂的设置。

三、MenuList Composition 组件详解

1. 什么是 MenuList Composition?

在某些场景下,默认的 Menu 组件基于 Popover 实现,可能不符合所有的设计需求,特别是在需要不同的定位策略或避免滚动阻塞时。这时,MenuList 组件就派上了用场。MenuList 是一个独立的菜单列表组件,它提供了更灵活的控制和自定义选项,开发者可以根据需要组合其他组件,比如 Popper 来实现不同的菜单展现方式。

2. MenuList Composition 的功能和用法

以下是一个使用 MenuList 组件结合 Popper 实现自定义菜单的例子:

import * as React from 'react';
import Button from '@mui/material/Button';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import Grow from '@mui/material/Grow';
import Paper from '@mui/material/Paper';
import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import Stack from '@mui/material/Stack';

export default function MenuListComposition() {
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null);

  const handleToggle = () => {
    setOpen((prevOpen) => !prevOpen);
  };

  const handleClose = (event) => {
    if (anchorRef.current && anchorRef.current.contains(event.target)) {
      return;
    }

    setOpen(false);
  };

  function handleListKeyDown(event) {
    if (event.key === 'Tab') {
      event.preventDefault();
      setOpen(false);
    } else if (event.key === 'Escape') {
      setOpen(false);
    }
  }

  // return focus to the button when we transitioned from !open -> open
  const prevOpen = React.useRef(open);
  React.useEffect(() => {
    if (prevOpen.current === true && open === false) {
      anchorRef.current.focus();
    }

    prevOpen.current = open;
  }, [open]);

  return (
    <Stack direction="row" spacing={2}>
      <div>
        <Button
          ref={anchorRef}
          id="composition-button"
          aria-controls={open ? 'composition-menu' : undefined}
          aria-expanded={open ? 'true' : undefined}
          aria-haspopup="true"
          onClick={handleToggle}
        >
          Dashboard
        </Button>
        <Popper
          open={open}
          anchorEl={anchorRef.current}
          role={undefined}
          placement="bottom-start"
          transition
          disablePortal
        >
          {({ TransitionProps, placement }) => (
            <Grow
              {...TransitionProps}
              style={{
                transformOrigin:
                  placement === 'bottom-start' ? 'left top' : 'left bottom',
              }}
            >
              <Paper>
                <ClickAwayListener onClickAway={handleClose}>
                  <MenuList
                    autoFocusItem={open}
                    id="composition-menu"
                    aria-labelledby="composition-button"
                    onKeyDown={handleListKeyDown}
                  >
                    <MenuItem onClick={handleClose}>Profile</MenuItem>
                    <MenuItem onClick={handleClose}>My account</MenuItem>
                    <MenuItem onClick={handleClose}>Logout</MenuItem>
                  </MenuList>
                </ClickAwayListener>
              </Paper>
            </Grow>
          )}
        </Popper>
      </div>
    </Stack>
  );
}

3. MenuList Composition 的关键点

  • 独立菜单控制MenuList 允许开发者更精细地控制菜单的行为,不再依赖 Popover 的默认功能,适合需要更多定制的场景。
  • 结合 Popper:通过与 Popper 组件结合,MenuList 可以实现与 Popover 不同的定位策略,避免了传统 Popover 对滚动的阻塞效果。

4. 菜单的关闭与键盘控制

在该示例中,ClickAwayListener 用于监控用户点击菜单外部的事件,以便关闭菜单。同时,通过监听键盘事件(如 TabEscape 键),开发者可以进一步增强用户的交互体验。这样可以保证在菜单关闭时,焦点重新回到触发按钮上,提升了可访问性。

四、Positioned Menu 与 MenuList Composition 的对比与适用场景

  • Positioned Menu:适合大多数情况下的简单菜单展示,基于 Popover 定位,易于实现和控制。
  • MenuList Composition:更适合复杂的场景,特别是在需要自定义定位或避免滚动阻塞时,通过 PopperMenuList 的组合可以实现灵活的布局和行为控制。

五、总结

Material-UI 中的 Positioned MenuMenuList Composition 组件为开发者提供了强大的菜单控制功能。通过灵活运用这些组件,开发者可以轻松实现各种复杂的菜单布局和交互逻辑,满足不同的设计需求。

推荐:


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peter-Lu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值