当我单击菜单以外的其他地方时如何关闭自定义菜单 [英] How to make a custom menu close when I click somewhere else other than the menu

查看:44
本文介绍了当我单击菜单以外的其他地方时如何关闭自定义菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要piggy带

To piggyback on a previous question, I'd like my menu to close whenever I click somewhere else other than the menu.

Currently, it will open/close when I click the "Hamburger Menu Button". It will close if I click a link on the menu or the menu itself but I'd also like to close when I "blur" away from it. This would be like if you got an alert message and you clicked somewhere else, the alert would close.

Here is the current CodeSandbox

The relevant code is in Header.tsx:

import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
  AppBar,
  Container,
  createStyles,
  IconButton,
  makeStyles,
  Theme,
  Toolbar,
  Typography
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import clsx from "clsx";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      "& a": {
        color: "white",
        textDecoration: "none"
      }
    },
    menuButton: {
      marginRight: theme.spacing(2),
      zIndex: 2
    },
    title: {
      flexGrow: 1,
      zIndex: 2
    },
    toolBar: {
      "& div": {
        transition: "left .1s"
      }
    },
    menu: {
      zIndex: 1,
      width: 200,
      height: "100%",
      position: "fixed",
      top: 48,
      transition: "left .1s",
      marginRight: theme.spacing(2),
      left: -200,
      background: "#3f51b5",
      "& div:first-element": {
        marginTop: 100
      }
    },
    menuOpen: {
      left: 0,
      transition: "left .1s"
    },
    menuClose: {
      left: -200,
      transition: "left .1s"
    },

    topMenu: {
      display: "flex",
      "& div": {
        marginLeft: theme.spacing(1)
      }
    }
  })
);
const UserMenu = () => {
  const classes = useStyles();
  const [menuOpen, setMenuOpen] = useState(false);
  const toggleMenu = () => setMenuOpen(!menuOpen);
  const handleMenuClick = () => toggleMenu();
  return (
    <>
      <IconButton
        edge="start"
        className={classes.menuButton}
        color="inherit"
        aria-label="menu"
        onClick={toggleMenu}
      >
        <MenuIcon />
      </IconButton>
      <div className={classes.toolBar}>
        <Container
          className={clsx(classes.menu, {
            [classes.menuOpen]: menuOpen,
            [classes.menuClose]: !menuOpen,
            [classes.toolBar]: true
          })}
          onClick={handleMenuClick}
        >
          <div>
            <Link to="#">My Profile</Link>
          </div>
          <div>
            <Link to="#">Account</Link>
          </div>
          <div>
            <Link to="#">Admin</Link>
          </div>
        </Container>
      </div>
    </>
  );
};

const Header: React.FC = ({ children }) => {
  const classes = useStyles();

  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar variant="dense">
        <UserMenu />
        <Typography variant="h6" className={classes.title}>
          <Link to="#">Widgets, LLC</Link>
        </Typography>
        <div className={classes.topMenu}>
          <div>
            <Link to="#">Sign out</Link>
          </div>
          <div>
            <Link to="#">One more</Link>
          </div>
        </div>
      </Toolbar>
    </AppBar>
  );
};

export default Header;

**Edit: ** This is the current Sandbox after applying Ryan's changes and moving the UserMenu into its own file.

解决方案

You can use Material-UI's ClickAwayListener for this. The only tricky part is avoiding an immediate close of your menu after clicking on the button to open the menu (because of the click event being handled by the button opening the menu and then the same click event being handled by the ClickAwayListener closing the menu). Typically you would want to avoid rendering the ClickAwayListener until the menu is open, but I think that might break the transition on the menu unless you did further changes. My example addresses this problem by calling event.stopPropagation() in the click handler for the menu button (handleOpen).

Here's a modified version of your code/sandbox demonstrating this:

import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
  AppBar,
  Container,
  ClickAwayListener,
  createStyles,
  IconButton,
  makeStyles,
  Theme,
  Toolbar,
  Typography
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import clsx from "clsx";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      "& a": {
        color: "white",
        textDecoration: "none"
      }
    },
    menuButton: {
      marginRight: theme.spacing(2),
      zIndex: 2
    },
    title: {
      flexGrow: 1,
      zIndex: 2
    },
    toolBar: {
      "& div": {
        transition: "left .1s"
      }
    },
    menu: {
      zIndex: 1,
      width: 200,
      height: "100%",
      position: "fixed",
      top: 48,
      transition: "left .1s",
      marginRight: theme.spacing(2),
      left: -200,
      background: "#3f51b5",
      "& div:first-element": {
        marginTop: 100
      }
    },
    menuOpen: {
      left: 0,
      transition: "left .1s"
    },
    menuClose: {
      left: -200,
      transition: "left .1s"
    },

    topMenu: {
      display: "flex",
      "& div": {
        marginLeft: theme.spacing(1)
      }
    }
  })
);
const UserMenu = () => {
  const classes = useStyles();
  const [menuOpen, setMenuOpen] = useState(false);
  const handleOpen = (event: React.MouseEvent) => {
    if (!menuOpen) {
      event.stopPropagation();
      setMenuOpen(true);
    }
  };
  const handleClose = (event: React.MouseEvent<any, MouseEvent>) => {
    if (menuOpen) {
      setMenuOpen(false);
    }
  };
  return (
    <>
      <IconButton
        edge="start"
        className={classes.menuButton}
        color="inherit"
        aria-label="menu"
        onClick={handleOpen}
      >
        <MenuIcon />
      </IconButton>
      <div className={classes.toolBar}>
        <ClickAwayListener onClickAway={handleClose}>
          <Container
            className={clsx(classes.menu, {
              [classes.menuOpen]: menuOpen,
              [classes.menuClose]: !menuOpen,
              [classes.toolBar]: true
            })}
            onClick={handleClose}
          >
            <div>
              <Link to="#">My Profile</Link>
            </div>
            <div>
              <Link to="#">Account</Link>
            </div>
            <div>
              <Link to="#">Admin</Link>
            </div>
          </Container>
        </ClickAwayListener>
      </div>
    </>
  );
};

const Header: React.FC = ({ children }) => {
  const classes = useStyles();

  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar variant="dense">
        <UserMenu />
        <Typography variant="h6" className={classes.title}>
          <Link to="#">Widgets, LLC</Link>
        </Typography>
        <div className={classes.topMenu}>
          <div>
            <Link to="#">Sign out</Link>
          </div>
          <div>
            <Link to="#">One more</Link>
          </div>
        </div>
      </Toolbar>
    </AppBar>
  );
};

export default Header;

这篇关于当我单击菜单以外的其他地方时如何关闭自定义菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆