Categories
Material UI

Material UI — Responsive and Draggable Dialogs

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 dialog boxes with Material UI.

Responsive Full-Screen Dialog

We can make a full-screen dialog responsive with the useMediaQuery hook.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import useMediaQuery from "@material-ui/core/useMediaQuery";
import { useTheme } from "@material-ui/core/styles";

export default function App() {
  const [open, setOpen] = React.useState(false);
  const theme = useTheme();
  const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));

  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Button variant="outlined" color="primary" onClick={handleClickOpen}>
        Open dialog
      </Button>
      <Dialog fullScreen={fullScreen} open={open} onClose={handleClose}>
        <DialogTitle>Dialog</DialogTitle>
        <DialogContent>
          <DialogContentText>lorem ipsum</DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary" autoFocus>
            ok
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

to add a responsive dialog that’s also full screen.

We used the useTheme hook to get the theme.

And with it, we use the useMediaQuery to get the breakpoint to pass in to the hook.

This means that the dialog box will be full screen if the screen’s width meets the sm breakpoint or smaller.

Otherwise, it’ll be a regular-sized dialog box.

Confirmation Dialogs

We can create a confirmation dialog to make use to select a choice before closing the dialog.

To create one, we can write:

import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import Dialog from "@material-ui/core/Dialog";
import RadioGroup from "@material-ui/core/RadioGroup";
import Radio from "@material-ui/core/Radio";
import FormControlLabel from "@material-ui/core/FormControlLabel";

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

function ConfirmationDialogRaw(props) {
  const { onClose, value: valueProp, open, ...other } = props;
  const [value, setValue] = React.useState(valueProp);
  const radioGroupRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) {
      setValue(valueProp);
    }
  }, [valueProp, open]);

  const handleEntering = () => {
    if (radioGroupRef.current != null) {
      radioGroupRef.current.focus();
    }
  };

  const handleCancel = () => {
    onClose();
  };

  const handleOk = () => {
    onClose(value);
  };

  const handleChange = event => {
    setValue(event.target.value);
  };

  return (
    <Dialog
      disableBackdropClick
      disableEscapeKeyDown
      maxWidth="xs"
      onEntering={handleEntering}
      open={open}
      {...other}
    >
      <DialogTitle>Fruit</DialogTitle>
      <DialogContent dividers>
        <RadioGroup
          ref={radioGroupRef}
          name="ringtone"
          value={value}
          onChange={handleChange}
        >
          {options.map(option => (
            <FormControlLabel
              value={option}
              key={option}
              control={<Radio />}
              label={option}
            />
          ))}
        </RadioGroup>
      </DialogContent>
      <DialogActions>
        <Button autoFocus onClick={handleCancel} color="primary">
          Cancel
        </Button>
        <Button onClick={handleOk} color="primary">
          Ok
        </Button>
      </DialogActions>
    </Dialog>
  );
}

export default function App() {
  const [open, setOpen] = React.useState(false);
  const [value, setValue] = React.useState("Done");

  const onClick = () => {
    setOpen(true);
  };

  const handleClose = newValue => {
    setOpen(false);

    if (newValue) {
      setValue(newValue);
    }
  };

  return (
    <div>
      <Button secondary={value} onClick={onClick}>
        Choose fruit
      </Button>

      <ConfirmationDialogRaw
        keepMounted
        open={open}
        onClose={handleClose}
        value={value}
      />
    </div>
  );
}

We added the ConfirmationDialogRaw component to display a dialog with various choices.

It has a list of choices rendered from the option array as a set of radio buttons.

Also, we have an OK and Cancel button below the radio buttons.

In the App component, we have the Button to let us open the dialog box.

Draggable Dialog

We can make a dialog draggable with the react-draggable library.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import Paper from "@material-ui/core/Paper";
import Draggable from "react-draggable";

function PaperComponent(props) {
  return (
    <Draggable
      handle="#draggable-dialog-title"
      cancel={'[class*="MuiDialogContent-root"]'}
    >
      <Paper {...props} />
    </Draggable>
  );
}

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Button variant="outlined" color="primary" onClick={handleClickOpen}>
        Open dialog
      </Button>
      <Dialog open={open} onClose={handleClose} PaperComponent={PaperComponent}>
        <DialogTitle style={{ cursor: "move" }} id="draggable-dialog-title">
          Title
        </DialogTitle>
        <DialogContent>
          <DialogContentText>lorem ipsum</DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            ok
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

to make a draggable dialog.

We created the PaperComponent to make it draggable.

The Draggable component from react-draggable lets us specify the draggable item by settle the handle prop with the selector with the item we want to make draggable.

We pass that into the PaperComponent prop of Dialog .

The selector has to be in the DialogTitle to make the dialog draggable.

Conclusion

We can make full-screen dialogs responsive.

They can also be made draggable.

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.