Categories
Material UI

Material UI — Progress Bars

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add progress bars with Material UI.

Linear Progress Bar

We can use the LinearProgress component to add a horizontal progress bar.

For example, we can write:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";

export default function App() {
  return (
    <div>
      <LinearProgress />
    </div>
  );
}

We just add it and it’ll show an animated progress bar.

To change the style, we can set the color property:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";

export default function App() {
  return (
    <div>
      <LinearProgress color="secondary" />
    </div>
  );
}

Now it’ll be pink.

Determinate Progress Bar

If we want to show a progress bar until something is done, we can set the value prop to control the progress.

We also need the variant prop set to determinate to set determinate progress.

For example, we can write:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";

export default function App() {
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress(oldProgress => {
        if (oldProgress === 100) {
          return 0;
        }
        return Math.min(oldProgress + 15, 100);
      });
    }, 500);

  return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      <LinearProgress variant="determinate" value={progress} />
    </div>
  );
}

to add a prigress state. which is a number we use to determine the progress.

The value ranges from 0 to 100.

The value is updated with the useEffect progress, which calls setProgress to update the progress.

If it’s less than 100, we keep adding 100 to it.

Otherwise, we set it to 100.

Then we get a progress bar that keeps moving back and forth.

Linear Buffer

We can set the variant to buffer to add a dotted line and a lighter line to the bar.

For example, we can write:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";

export default function App() {
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress(oldProgress => {
        if (oldProgress === 100) {
          return 0;
        }
        return Math.min(oldProgress + 15, 100);
      });
    }, 500);

  return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      <LinearProgress
        variant="buffer"
        value={progress}
        valueBuffer={progress * 1.1}
      />
    </div>
  );
}

to show a light bar to the right of the actual progress bar.

The rest of the unfilled space is filled with a dotted line.

Non-Standard Ranges

We can make the range non-standard by creating our own function to normalize the number to 100.

For example, we can write:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";

const MIN = 100;
const MAX = 200;
const normalize = value => ((value - MIN) * 100) / (MAX - MIN);

export default function App() {
  const [progress, setProgress] = React.useState(MIN);

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress(oldProgress => {
        if (oldProgress === MAX) {
          return 0;
        }
        return Math.min(oldProgress + 15, MAX);
      });
    }, 500);

  return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      <LinearProgress variant="determinate" value={normalize(progress)} />
    </div>
  );
}

We set the MIN and MAX constants to the values we want.

Then we define the normalize function to convert that to a range between 0 and 100.

And then we can do the same thing as the other determinate progress bars.

The only thing different is the value prop still has a value between 0 and 100 because we called the normalize function to convert to such.

Customized Progress

We can style the progress with the withStyles higher-order component.

For example, we can write:

import React from "react";
import LinearProgress from "@material-ui/core/LinearProgress";
import { withStyles } from "@material-ui/core/styles";

const BorderLinearProgress = withStyles(theme => ({
  root: {
    height: 10,
    borderRadius: 5
  },
  colorPrimary: {
    backgroundColor:
      theme.palette.grey[theme.palette.type === "light" ? 200 : 700]
  },
  bar: {
    borderRadius: 5,
    backgroundColor: "green"
  }
}))(LinearProgress);

export default function App() {
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress(oldProgress => {
        if (oldProgress === 100) {
          return 0;
        }
        return Math.min(oldProgress + 15, 100);
      });
    }, 500);

  return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      <BorderLinearProgress variant="determinate" value={progress} />
    </div>
  );
}

We used the withStyles higher-order component to change the color of the progress bar with the backgroundColor property of the bar class.

colorPrimary has the color of the unfilled part.

The height andborderRadius are also changed.

Conclusion

We can make a linear progress bar and use it either to show a progress bar forever or just use it to display the progress.

Categories
Material UI

Material UI — Paper and Card

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add papers and cards with Material UI.

Paper

Paper is a component that resembles papers in real life.

To add them, we can use the Paper component.

For example, we can write:

import React from "react";
import Paper from "@material-ui/core/Paper";

export default function App() {
  return (
    <>
      <Paper elevation={0}>foo</Paper>
      <Paper>foo</Paper>
      <Paper elevation={3}>foo</Paper>
    </>
  );
}

to add papers with various depths.

The depths are defined by the elevation prop.

Paper Variants

We can also add the variant prop to make the surface outlined.

For example, we can write:

import React from "react";
import Paper from "@material-ui/core/Paper";

export default function App() {
  return (
    <>
      <Paper variant="outlined" square>
        foo
      </Paper>
    </>
  );
}

We make the variant prop outlined to make it outlined.

Card

Cards contain content and actions about something.

For example, we can add one by writing:

import React from "react";
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";

export default function App() {
  return (
    <>
      <Card>
        <CardContent>
          <Typography color="textSecondary" gutterBottom>
            Word of the Day
          </Typography>
          <Typography variant="h5" component="h2">
            foo
          </Typography>
        </CardContent>
        <CardActions>
          <Button size="small">Learn More</Button>
        </CardActions>
      </Card>
    </>
  );
}

We add the Card with some CardContent .

To add styled text, we add some Typography component.

Then we added a CardAction to let us do something.

Outlined Card

To make the card outlined, we can add the variant prop and set the value to outlined .

For example, we can write:

import React from "react";
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";

export default function App() {
  return (
    <>
      <Card variant="outlined">
        <CardContent>
          <Typography color="textSecondary" gutterBottom>
            Word of the Day
          </Typography>
          <Typography variant="h5" component="h2">
            foo
          </Typography>
        </CardContent>
        <CardActions>
          <Button size="small">Learn More</Button>
        </CardActions>
      </Card>
    </>
  );
}

Now we see an outline around the box.

Complex Card

We can make a card with some content in it.

We make it expandable with the Collapse component in the card.

The in prop specifies that it’s expanded with the expanded value.

If it’s true , then we see the expanded text.

For example, we can write:

import React from "react";
import clsx from "clsx";
import Card from "@material-ui/core/Card";
import CardHeader from "@material-ui/core/CardHeader";
import CardMedia from "@material-ui/core/CardMedia";
import CardContent from "@material-ui/core/CardContent";
import CardActions from "@material-ui/core/CardActions";
import Collapse from "@material-ui/core/Collapse";
import IconButton from "@material-ui/core/IconButton";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import FavoriteIcon from "@material-ui/icons/Favorite";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";

const useStyles = makeStyles(theme => ({
  expand: {
    transform: "rotate(0deg)",
    marginLeft: "auto",
    transition: theme.transitions.create("transform", {
      duration: theme.transitions.duration.shortest
    })
  },
  media: {
    height: 0,
    paddingTop: "56.25%"
  }
}));

export default function App() {
  const classes = useStyles();
  const [expanded, setExpanded] = React.useState(false);

  const handleExpandClick = () => {
    setExpanded(!expanded);
  };

  return (
    <Card className={classes.root}>
      <CardHeader title="Cat" subheader="Small cat" />
      <CardMedia
        className={classes.media}
        image="https://placekitten.com/200/200"
        title="Cat"
      />
      <CardContent>
        <Typography variant="body2" color="textSecondary" component="p">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
          aliquet facilisis ligula sed gravida.
        </Typography>
      </CardContent>
      <CardActions disableSpacing>
        <IconButton>
          <FavoriteIcon />
        </IconButton>
        <IconButton
          className={clsx(classes.expand, {
            [classes.expandOpen]: expanded
          })}
          onClick={handleExpandClick}
        >
          <ExpandMoreIcon />
        </IconButton>
      </CardActions>
      <Collapse in={expanded} timeout="auto" unmountOnExit>
        <CardContent>
          <Typography paragraph>
            Aliquam efficitur sapien ac diam rutrum, ac venenatis nisi dictum.
          </Typography>
          <Typography paragraph>
            Integer eu lectus pulvinar, ornare ipsum eget
          </Typography>
        </CardContent>
      </Collapse>
    </Card>
  );
}

to make the card content in the Collapse component show when we click the expand more icon.

We also added the CardMedia to show a picture.

The image prop has the image.

title has the accessible text for the card media.

Conclusion

We can add a card with text and images.

Also, we can have content that shows when we click on the expand button.

Categories
Material UI

Material UI — Mobile Steppers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add mobile steppers with Material UI.

Mobile Stepper

We can create a mobile stepper with the MobileStepper component.

For instance, we can write:

import React from "react";
import MobileStepper from "@material-ui/core/MobileStepper";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Paper from "@material-ui/core/Paper";
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight";
import KeyboardArrowLeft from "@material-ui/icons/KeyboardArrowLeft";

const steps = [
  {
    label: "cat 1",
    imgPath: "http://placekitten.com/200/200"
  },
  {
    label: "cat 2",
    imgPath: "http://placekitten.com/199/199"
  },
  {
    label: "cat 3",
    imgPath: "http://placekitten.com/201/201"
  }
];

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const maxSteps = steps.length;

  const handleNext = () => {
    setActiveStep(prevActiveStep => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  return (
    <div>
      <Paper square elevation={0}>
        <Typography>{steps[activeStep].label}</Typography>
      </Paper>
      <img src={steps[activeStep].imgPath} alt={steps[activeStep].label} />
      <MobileStepper
        steps={maxSteps}
        position="static"
        variant="text"
        activeStep={activeStep}
        nextButton={
          <Button
            size="small"
            onClick={handleNext}
            disabled={activeStep === maxSteps - 1}
          >
            Next
            <KeyboardArrowRight />
          </Button>
        }
        backButton={
          <Button size="small" onClick={handleBack} disabled={activeStep === 0}>
            <KeyboardArrowLeft />
            Back
          </Button>
        }
      />
    </div>
  );
}

to add a mobile stepper.

We used the MobileStepper component to create the stepper.

The Paper has the paper box for the text.

Then we have an image below that.

Then we have the MobileStepper with the navigation buttons.

We pass in a button for the nextButtin and backButton props.

The next button calls the handleNext function when it’s clicked.

The back button calls the handleBack function when it’s clicked.

This will let us move to the next step and back respectively.

Carousel Effect

To make the stepper move through the slides automatically, w can add the AutoPlaySwipeableViews component.

For example, we can write:

import React from "react";
import MobileStepper from "@material-ui/core/MobileStepper";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Paper from "@material-ui/core/Paper";
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight";
import KeyboardArrowLeft from "@material-ui/icons/KeyboardArrowLeft";
import SwipeableViews from "react-swipeable-views";
import { autoPlay } from "react-swipeable-views-utils";

const AutoPlaySwipeableViews = autoPlay(SwipeableViews);

const steps = [
  {
    label: "cat 1",
    imgPath: "http://placekitten.com/200/200"
  },
  {
    label: "cat 2",
    imgPath: "http://placekitten.com/199/199"
  },
  {
    label: "cat 3",
    imgPath: "http://placekitten.com/201/201"
  }
];

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const maxSteps = steps.length;

  const handleNext = () => {
    setActiveStep(prevActiveStep => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleStepChange = step => {
    setActiveStep(step);
  };

  return (
    <div>
      <Paper square elevation={0}>
        <Typography>{steps[activeStep].label}</Typography>
      </Paper>
      <AutoPlaySwipeableViews
        axis="x"
        index={activeStep}
        onChangeIndex={handleStepChange}
        enableMouseEvents
      >
        {steps.map((step, index) => (
          <div key={step.label}>
            {Math.abs(activeStep - index) <= 2 ? (
              <img src={step.imgPath} alt={step.label} />
            ) : null}
          </div>
        ))}
      </AutoPlaySwipeableViews>
      <MobileStepper
        steps={maxSteps}
        position="static"
        variant="text"
        activeStep={activeStep}
        nextButton={
          <Button
            size="small"
            onClick={handleNext}
            disabled={activeStep === maxSteps - 1}
          >
            Next
            <KeyboardArrowRight />
          </Button>
        }
        backButton={
          <Button size="small" onClick={handleBack} disabled={activeStep === 0}>
            <KeyboardArrowLeft />
            Back
          </Button>
        }
      />
    </div>
  );
}

to make the slides autoplay.

We added the react-swipeable-views and react-swipeable-views-utils libraries to let us add the autoplay capabilities.

The AutoPlaySwipeableViews component we created from those libraries are added to our JSX code to make the view autoplay.

Progress Bar

We can add a progress bar if we have many steps.

To add a stepper with a progress bar, we can write:

import React from "react";
import MobileStepper from "@material-ui/core/MobileStepper";
import Button from "@material-ui/core/Button";
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight";
import KeyboardArrowLeft from "@material-ui/icons/KeyboardArrowLeft";

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);

  const handleNext = () => {
    setActiveStep(prevActiveStep => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  return (
    <MobileStepper
      variant="progress"
      steps={10}
      position="static"
      activeStep={activeStep}
      nextButton={
        <Button size="small" onClick={handleNext} disabled={activeStep === 9}>
          Next
          <KeyboardArrowRight />
        </Button>
      }
      backButton={
        <Button size="small" onClick={handleBack} disabled={activeStep === 0}>
          <KeyboardArrowLeft />
          Back
        </Button>
      }
    />
  );
}

We use the MobileStepper component again.

The nextButton and backButton props are the same.

The difference is that we have the steps prop.

We also have to disable the buttons when we reach the max step number for the next button.

And we do the same when we meet the min step number for the back button.

Conclusion

We can add mobile steppers for carousels or add a more compact stepper component.

Categories
Material UI

Material UI — Menu Customization

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize menus to Material UI.

Customized Menus

We can customize our menu with our styles.

For example, we can write:

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import InboxIcon from "@material-ui/icons/MoveToInbox";

const StyledMenu = withStyles({
  paper: {
    border: "1px solid #d3d4d5"
  }
})(props => (
  <Menu
    elevation={0}
    getContentAnchorEl={null}
    anchorOrigin={{
      vertical: "bottom"
    }}
    transformOrigin={{
      vertical: "top"
    }}
    {...props}
  />
));

const StyledMenuItem = withStyles(theme => ({
  root: {
    "&:focus": {
      backgroundColor: theme.palette.primary.main,
      "& .MuiListItemIcon-root, & .MuiListItemText-primary": {
        color: theme.palette.common.white
      }
    }
  }
}))(MenuItem);

export default function App() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <div>
      <Button variant="contained" color="primary" onClick={handleClick}>
        Open Menu
      </Button>
      <StyledMenu open={Boolean(anchorEl)}>
        <StyledMenuItem onClick={handleClose}>
          <ListItemIcon>
            <InboxIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText primary="home" />
        </StyledMenuItem>
      </StyledMenu>
    </div>
  );
}

to use the withStyles higher-order component to style the menu.

To style the menu items, we can style it with the withStyles higher-order component.

We set the color with one from the theme parameter.

Then we can use them all in our App component.

Max Height Menus

We can set the height of the Menu with the style property.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";

const options = ["apple", "orange", "grape", "banana", "pear", "mango"];

export default function App() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <div>
      <Button variant="contained" color="primary" onClick={handleClick}>
        Open Menu
      </Button>
      <Menu
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
        PaperProps={{
          style: {
            maxHeight: `200px`
          }
        }}
      >
        {options.map(option => (
          <MenuItem
            key={option}
            selected={option === "apple"}
            onClick={handleClose}
          >
            {option}
          </MenuItem>
        ))}
      </Menu>
    </div>
  );
}

We set the maxHeight property in the PaperProps to set the height of the menu.

Change Transition

We can add transitions when our menu is opening.

It takes a TransitionComponent prop to let us add the transition we want.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import Fade from "@material-ui/core/Fade";

export default function App() {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const open = Boolean(anchorEl);

  const handleClick = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <div>
      <Button onClick={handleClick}>Open</Button>
      <Menu
        anchorEl={anchorEl}
        keepMounted
        open={open}
        onClose={handleClose}
        TransitionComponent={Fade}
      >
        <MenuItem onClick={handleClose}>home</MenuItem>
        <MenuItem onClick={handleClose}>profile</MenuItem>
        <MenuItem onClick={handleClose}>logout</MenuItem>
      </Menu>
    </div>
  );
}

to add a fade transition when the menu is opening with the TransitionComponent prop set to Fade .

Context Menu

To add a context menu, we can listen to the onContextMenu prop.

Then we can show the context menu where we right-click by setting the mouse coordinates in the anchorPosition prop.

For instance, we can write:

import React from "react";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import Typography from "@material-ui/core/Typography";

const initialState = {
  mouseX: null,
  mouseY: null
};
export default function App() {
  const [state, setState] = React.useState(initialState);

  const handleClick = event => {
    event.preventDefault();
    setState({
      mouseX: event.clientX - 2,
      mouseY: event.clientY - 4
    });
  };

  const handleClose = () => {
    setState(initialState);
  };

  return (
    <div onContextMenu={handleClick} style={{ cursor: "context-menu" }}>
      <Typography>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ipsum
        purus, bibendum sit amet vulputate eget, porta semper ligula. Donec
        bibendum vulputate erat, ac fringilla mi finibus nec. Donec ac dolor sed
        dolor porttitor blandit vel vel purus. Fusce vel malesuada ligula. Nam
        quis vehicula ante, eu finibus est. Proin ullamcorper fermentum orci,
        quis finibus massa.
      </Typography>
      <Menu
        keepMounted
        open={state.mouseY !== null}
        onClose={handleClose}
        anchorReference="anchorPosition"
        anchorPosition={
          state.mouseY !== null && state.mouseX !== null
            ? { top: state.mouseY, left: state.mouseX }
            : undefined
        }
      >
        <MenuItem onClick={handleClose}>select</MenuItem>
        <MenuItem onClick={handleClose}>paste</MenuItem>
        <MenuItem onClick={handleClose}>copy</MenuItem>
        <MenuItem onClick={handleClose}>save</MenuItem>
      </Menu>
    </div>
  );
}

to add a div with some text.

And we add a context menu with the Menu component.

We add keepMounted to keep it in the DOM.

open is true when mouseY isn’t null .

It’s set when we right-click within the handleClick function.

anchorPosition lets us set the mousex and mouseY to make the menu open with the given mouse coordinates.

Conclusion

We can add menus with styles.

Also, we can add context menus.

Transitions can also be added to change effects when the menu opens.

Categories
Material UI

Material UI — Links and Menus

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add links and menus to Material UI.

Simple Links

We can add links with the Link component.

For instance, we can write:

import React from "react";
import Link from "@material-ui/core/Link";

export default function App() {
  const preventDefault = event => event.preventDefault();

  return (
    <div>
      <Link href="#" onClick={preventDefault}>
        Link
      </Link>
    </div>
  );
}

We add a link with the href to go the URL we want when we click it.

onClick lets us pass in a click handler to change the behavior of the link.

We can also change the color to change the color of the link.

For example, we can write:

import React from "react";
import Link from "@material-ui/core/Link";

export default function App() {
  const preventDefault = event => event.preventDefault();

  return (
    <div>
      <Link href="#" onClick={preventDefault} color="primary">
        Link
      </Link>
    </div>
  );
}

We set the color to primary to make it purple.

Also, we can pass a value to the variant prop to change the styles:

import React from "react";
import Link from "@material-ui/core/Link";
import Typography from "@material-ui/core/Typography";

export default function App() {
  const preventDefault = event => event.preventDefault();

  return (
    <div>
      <Typography>
        <Link href="#" onClick={preventDefault} variant="inherit">
          Link
        </Link>
      </Typography>
    </div>
  );
}

Menus

We can add a menu with the Menu component.

Inside it, we can add items with the MenuItem components.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";

export default function App() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };
  return (
    <div>
      <Button onClick={handleClick}>Open</Button>
      <Menu
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <MenuItem onClick={handleClose}>home</MenuItem>
        <MenuItem onClick={handleClose}>profile</MenuItem>
        <MenuItem onClick={handleClose}>logout</MenuItem>
      </Menu>
    </div>
  );
}

to add a menu.

We have a Button that has the onClick prop set to the handleClick function.

It sets the anchor element to element so that we can open the menu.

We set the open prop of the menu to the anchor element.

The anchor element would be the button as indicated in the handleClick function.

This way, we can determine if the button is clicked and we can open the menu if it’s set.

To close the menu, we set the anchorEl to null so that open will have false passed in.

Selected Menus

We can keep a menu item selected.

To do that, we can set the selected prop to true if the item is selected.

For example, we can write:

import React from "react";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";

const options = ["apple", "orange", "grape"];

export default function App() {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const [selectedIndex, setSelectedIndex] = React.useState(1);

  const handleClickListItem = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleMenuItemClick = (event, index) => {
    setSelectedIndex(index);
    setAnchorEl(null);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  return (
    <div>
      <List component="nav">
        <ListItem button onClick={handleClickListItem}>
          open
        </ListItem>
      </List>
      <Menu
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        {options.map((option, index) => (
          <MenuItem
            key={option}
            selected={index === selectedIndex}
            onClick={event => handleMenuItemClick(event, index)}
          >
            {option}
          </MenuItem>
        ))}
      </Menu>
    </div>
  );
}

to add a menu.

We keep the index of the selected item with the selectedIndex .

It’s set when we click on the MenuItem .

We have the selected prop that has a boolean expression to compare the index of the item against the selectedIndex value to determine which value is selected.

Customized Menu

We can create our own menu with the Popper , Paper and ClickAwayListener components.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import Grow from "@material-ui/core/Grow";
import Paper from "@material-ui/core/Paper";
import Popper from "@material-ui/core/Popper";
import MenuItem from "@material-ui/core/MenuItem";
import MenuList from "@material-ui/core/MenuList";

export default function App() {
  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);
    }
  }

  const prevOpen = React.useRef(open);
  React.useEffect(() => {
    if (prevOpen.current === true && open === false) {
      anchorRef.current.focus();
    }

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

  return (
    <div>
      <Button ref={anchorRef} onClick={handleToggle}>
        menu
      </Button>
      <Popper open={open} anchorEl={anchorRef.current} transition disablePortal>
        {({ TransitionProps, placement }) => (
          <Grow
            {...TransitionProps}
            style={{
              transformOrigin:
                placement === "bottom" ? "center top" : "center bottom"
            }}
          >
            <Paper>
              <ClickAwayListener onClickAway={handleClose}>
                <MenuList autoFocusItem={open} onKeyDown={handleListKeyDown}>
                  <MenuItem onClick={handleClose}>home</MenuItem>
                  <MenuItem onClick={handleClose}>profile</MenuItem>
                  <MenuItem onClick={handleClose}>logout</MenuItem>
                </MenuList>
              </ClickAwayListener>
            </Paper>
          </Grow>
        )}
      </Popper>
    </div>
  );
}

We add the Button to toggle the menu.

The Popper has the actual menu.

The Grow component adds the transition for the menu with the TransitionProps .

We can set the transformOrigin to set how the menu is placed.

Paper has holds the ClickAwayListener so that the menu is closed when we click outside the menu.

MenuList has the menu entries.

handleClose sets the open state to false to close the menu.

We also have the handleListKeyDown function to handle Tab key presses.

We close the menu when it’s pressed.

Conclusion

We can add links and menus easily with the Link and Menu components.