Categories
Material UI

Material UI — Progress Spinner 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 add progress spinner with Material UI.

Interactive Integration

We can add the spinner to buttons and floating action buttons.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";
import { pink } from "@material-ui/core/colors";
import Fab from "@material-ui/core/Fab";
import CheckIcon from "@material-ui/icons/Check";
import SaveIcon from "@material-ui/icons/Save";
import clsx from "clsx";

const useStyles = makeStyles(theme => ({
  root: {
    display: "flex",
    alignItems: "center"
  },
  wrapper: {
    margin: theme.spacing(1),
    position: "relative"
  },
  fabProgress: {
    color: pink[500],
    position: "absolute",
    top: -6,
    left: -6,
    zIndex: 1
  }
}));

export default function App() {
  const classes = useStyles();
  const [loading, setLoading] = React.useState(false);
  const [success, setSuccess] = React.useState(false);
  const timer = React.useRef();

const buttonClassname = clsx({
    [classes.buttonSuccess]: success
  });

React.useEffect(() => {
    return () => {
      clearTimeout(timer.current);
    };
  }, []);

const handleButtonClick = () => {
    if (!loading) {
      setSuccess(false);
      setLoading(true);
      timer.current = setTimeout(() => {
        setSuccess(true);
        setLoading(false);
      }, 1000);
    }
  };

  return (
    <div className={classes.root}>
      <div className={classes.wrapper}>
        <Fab
          aria-label="save"
          color="primary"
          className={buttonClassname}
          onClick={handleButtonClick}
        >
          {success ? <CheckIcon /> : <SaveIcon />}
        </Fab>
        {loading && (
          <CircularProgress size={68} className={classes.fabProgress} />
        )}
      </div>
    </div>
  );
}

to add a loading spinner around a floating action button.

We have to set the fabProgress class to move the spinner so that it wraps around the floating action button.

Also, we have to change the spacing with the wrapper class to make the button align with the spinner.

When we click on the button, the handleButtonClick function is run.

This sets the loading state to true .

This will trigger the spinner to show.

Then after 1 second, we set the loading state to false in the useEffect callback.

The success state would be set to true there and we see the checkmark icon.

Likewise, we can do the same with buttons.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";
import { pink } from "@material-ui/core/colors";
import Button from "@material-ui/core/Button";
import clsx from "clsx";

const useStyles = makeStyles(theme => ({
  root: {
    display: "flex",
    alignItems: "center"
  },
  wrapper: {
    margin: theme.spacing(1),
    position: "relative"
  },
  buttonProgress: {
    color: pink[500],
    position: "absolute",
    top: "50%",
    left: "50%",
    marginTop: -12,
    marginLeft: -12
  }
}));

export default function App() {
  const classes = useStyles();
  const [loading, setLoading] = React.useState(false);
  const [success, setSuccess] = React.useState(false);
  const timer = React.useRef();

  const buttonClassname = clsx({
    [classes.buttonSuccess]: success
  });

  React.useEffect(() => {
    return () => {
      clearTimeout(timer.current);
    };
  }, []);

  const handleButtonClick = () => {
    if (!loading) {
      setSuccess(false);
      setLoading(true);
      timer.current = setTimeout(() => {
        setSuccess(true);
        setLoading(false);
      }, 1000);
    }
  };

  return (
    <div className={classes.root}>
      <div className={classes.wrapper}>
        <Button
          variant="contained"
          color="primary"
          className={buttonClassname}
          disabled={loading}
          onClick={handleButtonClick}
        >
          save
        </Button>
        {loading && (
          <CircularProgress size={24} className={classes.buttonProgress} />
        )}
      </div>
    </div>
  );
}

to add a save button with a spinner in the middle.

The logic is the same as the floating action button.

But we have different styles for the spinner to position it in the button.

Circular Spinner with Label

We can display the progress within the circular spinner.

For example, we can write:

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

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

  React.useEffect(() => {
    const timer = setInterval(() => {
      setProgress(prevProgress =>
        prevProgress >= 100 ? 10 : prevProgress + 10
      );
    }, 800);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      <Box position="relative" display="inline-flex">
        <CircularProgress variant="static" value={progress} />
        <Box
          top={0}
          left={0}
          bottom={0}
          right={0}
          position="absolute"
          display="flex"
          alignItems="center"
          justifyContent="center"
        >
          <Typography
            variant="caption"
            component="div"
            color="textSecondary"
          >{`${Math.round(progress)}%`}</Typography>
        </Box>
      </Box>
    </div>
  );
}

to add a progress spinner with a number of animation inside it to show the progress.

We just put a Box to show the text.

The Box has an absolute position and we set alignItems and justifyContent to center to center the text in the spinner.

Conclusion

We can add spinners within buttons or around floating action buttons.

Also, we can display the progress inside the circular progress spinner.

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.