score:1

Accepted answer

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;

Edit close menu on click away


Related Query

More Query from same tag