Categories
Material UI

Material UI — Tooltips

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

Simple Tooltips

We can add simple tooltips that display when we hover over another component.

For example, we can add a floating action button with the Fab component.

Then we surround that with a Tooltip to display a tooltip.

For example, we can write:

import React from "react";
import AddIcon from "@material-ui/icons/Add";
import Fab from "@material-ui/core/Fab";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
      <Tooltip title="Add Task">
        <Fab color="primary">
          <AddIcon />
        </Fab>
      </Tooltip>
    </div>
  );
}

to add tooltips with the Tooltip component.

We can surround it around an IconButton or a Fab .

The title has th content that’ll be displayed in the tooltip.

Positioned Tooltips

We can put the tooltip in the position we want.

For example, we can write:

import React from "react";
import AddIcon from "@material-ui/icons/Add";
import Fab from "@material-ui/core/Fab";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" placement="left-end">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
      <Tooltip title="Add Task" placement="left-start">
        <Fab color="primary">
          <AddIcon />
        </Fab>
      </Tooltip>
    </div>
  );
}

to add a placement prop to each tooltip to change the placement.

Customized Tooltips

We can style our tooltips with the withStyles function.

For example, we can write:

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const LightTooltip = withStyles(theme => ({
  tooltip: {
    backgroundColor: theme.palette.common.white,
    color: "green",
    boxShadow: theme.shadows[1],
    fontSize: 11
  }
}))(Tooltip);

export default function App() {
  return (
    <div>
      <LightTooltip title="Delete File" placement="left-end">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </LightTooltip>
    </div>
  );
}

We made the backgroundColor white and the content color green.

Also, we change the fontSize to 11.

We can also use the makeStyles function to create classes that we can pass into the tooltip.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const useStyles = makeStyles(theme => ({
  arrow: {
    color: theme.palette.common.black
  },
  tooltip: {
    backgroundColor: theme.palette.common.black
  }
}));

function BlackTooltip(props) {
  const classes = useStyles();

  return <Tooltip arrow classes={classes} {...props} />;
}

export default function App() {
  return (
    <div>
      <BlackTooltip title="Delete File">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </BlackTooltip>
    </div>
  );
}

We created a BlackTooltip component that adds some classes to the Tooltip .

Now we have a black tooltip with a black arrow color as indicated in the arrow and tooltip properties.

Arrow Tooltips

To add an arrow to a tooltip, we can add the arrow prop.

For example, we can write;

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" arrow>
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
    </div>
  );
}

We use the arrow prop on the Tooltip to add it.

Custom Child Element

To add a custom child element, we’ve to use the forwardRef and return our component inside the callback.

This way, the DOM event listeners for the element are applied.

For example, we can write:

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const Content = React.forwardRef((props, ref) => {
  return (
    <div {...props} ref={ref}>
      <IconButton>
        <DeleteIcon />
      </IconButton>
    </div>
  );
});

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" arrow>
        <Content />
      </Tooltip>
    </div>
  );
}

to create a Content component that we can put between the Tooltip tags.

We put our content inside the forwardRef callback so the tooltip will show properly.

Conclusion

We can create tooltips with various styles and positions.

The content can also be changed to show what we want.

The styling can be changed in various ways.

Categories
Material UI

Material UI — Tables

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

Dense Table

We can change the size prop of the table to small to make the table denser.

For example, we can write:

import React from "react";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";

function createData(firstName, lastName, age) {
  return { firstName, lastName, age };
}

const rows = [
  createData("james", "smith", 20),
  createData("mary", "jones", 30),
  createData("may", "wong", 30)
];

export default function App() {
  return (
    <TableContainer component={Paper}>
      <Table size="small">
        <TableHead>
          <TableRow>
            <TableCell>first name</TableCell>
            <TableCell>last name</TableCell>
            <TableCell align="right">age</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map(row => (
            <TableRow key={row.firstName}>
              <TableCell component="th" scope="row">
                {row.firstName}
              </TableCell>
              <TableCell>{row.lastName}</TableCell>
              <TableCell align="right">{row.age}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

then we display the table rows in a denser way than the default.

Sorting and Selecting

We can add sorting with the TableSortLabel component.

For example, we can write:

import React from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { lighten, makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TablePagination from "@material-ui/core/TablePagination";
import TableRow from "@material-ui/core/TableRow";
import TableSortLabel from "@material-ui/core/TableSortLabel";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Checkbox from "@material-ui/core/Checkbox";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

import DeleteIcon from "@material-ui/icons/Delete";
import FilterListIcon from "@material-ui/icons/FilterList";

function createData(name, age) {
  return { name, age };
}

const rows = [
  createData("james", 30),
  createData("mary", 45),
  createData("alex", 26)
];

function descendingComparator(a, b, orderBy) {
  if (b[orderBy] < a[orderBy]) {
    return -1;
  }
  if (b[orderBy] > a[orderBy]) {
    return 1;
  }
  return 0;
}

function getComparator(order, orderBy) {
  return order === "desc"
    ? (a, b) => descendingComparator(a, b, orderBy)
    : (a, b) => -descendingComparator(a, b, orderBy);
}

function stableSort(array, comparator) {
  const stabilizedThis = array.map((el, index) => [el, index]);
  stabilizedThis.sort((a, b) => {
    const order = comparator(a[0], b[0]);
    if (order !== 0) return order;
    return a[1] - b[1];
  });
  return stabilizedThis.map(el => el[0]);
}

const headCells = [
  {
    id: "name",
    label: "name"
  },
  { id: "age", numeric: true, label: "age" }
];

function EnhancedTableHead(props) {
  const {
    classes,
    onSelectAllClick,
    order,
    orderBy,
    numSelected,
    rowCount,
    onRequestSort
  } = props;
  const createSortHandler = property => event => {
    onRequestSort(event, property);
  };

  return (
    <TableHead>
      <TableRow>
        <TableCell padding="checkbox">
          <Checkbox
            indeterminate={numSelected > 0 && numSelected < rowCount}
            checked={rowCount > 0 && numSelected === rowCount}
            onChange={onSelectAllClick}
            inputProps={{ "aria-label": "select all desserts" }}
          />
        </TableCell>
        {headCells.map(headCell => (
          <TableCell
            key={headCell.id}
            align={headCell.numeric ? "right" : "left"}
            padding={headCell.disablePadding ? "none" : "default"}
            sortDirection={orderBy === headCell.id ? order : false}
          >
            <TableSortLabel
              active={orderBy === headCell.id}
              direction={orderBy === headCell.id ? order : "asc"}
              onClick={createSortHandler(headCell.id)}
            >
              {headCell.label}
              {orderBy === headCell.id ? (
                <span className={classes.visuallyHidden}>
                  {order === "desc" ? "sorted descending" : "sorted ascending"}
                </span>
              ) : null}
            </TableSortLabel>
          </TableCell>
        ))}
      </TableRow>
    </TableHead>
  );
}

EnhancedTableHead.propTypes = {
  classes: PropTypes.object.isRequired,
  numSelected: PropTypes.number.isRequired,
  onRequestSort: PropTypes.func.isRequired,
  onSelectAllClick: PropTypes.func.isRequired,
  order: PropTypes.oneOf(["asc", "desc"]).isRequired,
  orderBy: PropTypes.string.isRequired,
  rowCount: PropTypes.number.isRequired
};

const useToolbarStyles = makeStyles(theme => ({
  root: {
    paddingLeft: theme.spacing(2),
    paddingRight: theme.spacing(1)
  },
  highlight:
    theme.palette.type === "light"
      ? {
          color: theme.palette.secondary.main,
          backgroundColor: lighten(theme.palette.secondary.light, 0.85)
        }
      : {
          color: theme.palette.text.primary,
          backgroundColor: theme.palette.secondary.dark
        },
  title: {
    flex: "1 1 100%"
  }
}));

const EnhancedTableToolbar = props => {
  const classes = useToolbarStyles();
  const { numSelected } = props;

return (
    <Toolbar
      className={clsx(classes.root, {
        [classes.highlight]: numSelected > 0
      })}
    >
      {numSelected > 0 ? (
        <Typography
          className={classes.title}
          color="inherit"
          variant="subtitle1"
          component="div"
        >
          {numSelected} selected
        </Typography>
      ) : (
        <Typography
          className={classes.title}
          variant="h6"
          id="tableTitle"
          component="div"
        >
          Names
        </Typography>
      )}

{numSelected > 0 ? (
        <Tooltip title="Delete">
          <IconButton aria-label="delete">
            <DeleteIcon />
          </IconButton>
        </Tooltip>
      ) : (
        <Tooltip title="Filter list">
          <IconButton aria-label="filter list">
            <FilterListIcon />
          </IconButton>
        </Tooltip>
      )}
    </Toolbar>
  );
};

EnhancedTableToolbar.propTypes = {
  numSelected: PropTypes.number.isRequired
};

const useStyles = makeStyles(theme => ({
  root: {
    width: "100%"
  },
  paper: {
    width: "100%",
    marginBottom: theme.spacing(2)
  },
  table: {
    minWidth: 750
  },
  visuallyHidden: {
    border: 0,
    clip: "rect(0 0 0 0)",
    height: 1,
    margin: -1,
    overflow: "hidden",
    padding: 0,
    position: "absolute",
    top: 20,
    width: 1
  }
}));

export default function App() {
  const classes = useStyles();
  const [order, setOrder] = React.useState("asc");
  const [orderBy, setOrderBy] = React.useState("calories");
  const [selected, setSelected] = React.useState([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);

  const handleRequestSort = (event, property) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = event => {
    if (event.target.checked) {
      const newSelecteds = rows.map(n => n.name);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event, name) => {
    const selectedIndex = selected.indexOf(name);
    let newSelected = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, name);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
        selected.slice(0, selectedIndex),
        selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = event => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const isSelected = name => selected.indexOf(name) !== -1;

  const emptyRows =
    rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <EnhancedTableToolbar numSelected={selected.length} />
        <TableContainer>
          <Table className={classes.table}>
            <EnhancedTableHead
              classes={classes}
              numSelected={selected.length}
              order={order}
              orderBy={orderBy}
              onSelectAllClick={handleSelectAllClick}
              onRequestSort={handleRequestSort}
              rowCount={rows.length}
            />
            <TableBody>
              {stableSort(rows, getComparator(order, orderBy))
                .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
                .map((row, index) => {
                  const isItemSelected = isSelected(row.name);

return (
                    <TableRow
                      hover
                      onClick={event => handleClick(event, row.name)}
                      role="checkbox"
                      aria-checked={isItemSelected}
                      tabIndex={-1}
                      key={row.name}
                      selected={isItemSelected}
                    >
                      <TableCell padding="checkbox">
                        <Checkbox checked={isItemSelected} />
                      </TableCell>
                      <TableCell component="th" scope="row" padding="none">
                        {row.name}
                      </TableCell>
                      <TableCell align="right">{row.age}</TableCell>
                    </TableRow>
                  );
                })}
              {emptyRows > 0 && (
                <TableRow>
                  <TableCell />
                </TableRow>
              )}
            </TableBody>
          </Table>
        </TableContainer>
        <TablePagination
          rowsPerPageOptions={[5, 10, 25]}
          component="div"
          count={rows.length}
          rowsPerPage={rowsPerPage}
          page={page}
          onChangePage={handleChangePage}
          onChangeRowsPerPage={handleChangeRowsPerPage}
        />
      </Paper>
    </div>
  );
}

to add a table with sorting and selection.

We started with the example from https://material-ui.com/components/tables/#sorting-amp-selecting.

We have the stableSort function to do the sorting.

And we have the checkbox on the row to show let us select items.

We also added a toolbar to the bottom to let us select the number of rows to display with the TablePagination component.

The EnhancedTableHead has the table heading with sorting.

We used the TableSortLabel to let us do the sorting.

Conclusion

We can add dense tables and tables with sorting with the built-in components.

Categories
Material UI

Material UI — Popovers

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

Simple Popover

Popovers let us display some content over other things.

To add it, we can use the Popover component.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";

const useStyles = makeStyles(theme => ({
  typography: {
    padding: theme.spacing(3)
  }
}));

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

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

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

  const open = Boolean(anchorEl);

  return (
    <div>
      <Button variant="contained" color="primary" onClick={handleClick}>
        Open Popover
      </Button>
      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center"
        }}
      >
        <Typography className={classes.typography}>Popover content.</Typography>
      </Popover>
    </div>
  );
}

to add a button to open the popover and the popover itself.

The anchorEl prop has the anchor element of the popover, which is the button itself.

anchorOrigin specifies where relative to the button do we display the popover.

transformOrigin lets us change the position of the popover itself.

open sets whether the popover ios open or not.

If the anchorEl is set, then it’s open.

Otherwise, it’s not open.

onClose is a function that runs when the popover closes.

Mouse Over Interaction

We can open a popover when we move our mouse over an element.

For example, we can write:

import React from "react";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
  popover: {
    pointerEvents: "none"
  },
  paper: {
    padding: theme.spacing(3)
  }
}));

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

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

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

  const open = Boolean(anchorEl);

  return (
    <div>
      <Typography
        onMouseEnter={handlePopoverOpen}
        onMouseLeave={handlePopoverClose}
      >
        Hover me
      </Typography>
      <Popover
        className={classes.popover}
        classes={{
          paper: classes.paper
        }}
        open={open}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
        onClose={handlePopoverClose}
        disableRestoreFocus
      >
        <Typography>some popover.</Typography>
      </Popover>
    </div>
  );
}

We pass in functions to the onMouseEnter prop to let us open the popover when we hover over the text.

Similarly, we pass in a function to the onMouseLeave prop to close the modal when our mouse leaves the element respectively.

The other parts are the same as the previous example.

PopupState Helper

To make managing the popover state easier, we can use the material-ui-popup-state library to manage the state.

To install it, we run:

npm install --save material-ui-popup-state

Then we can write:

import React from "react";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import Button from "@material-ui/core/Button";
import Popover from "@material-ui/core/Popover";
import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state";

export default function App() {
  return (
    <PopupState variant="popover">
      {popupState => (
        <div>
          <Button
            variant="contained"
            color="primary"
            {...bindTrigger(popupState)}
          >
            Open Popover
          </Button>
          <Popover
            {...bindPopover(popupState)}
            anchorOrigin={{
              vertical: "bottom",
              horizontal: "center"
            }}
            transformOrigin={{
              vertical: "top",
              horizontal: "center"
            }}
          >
            <Box p={3}>
              <Typography>some popover.</Typography>
            </Box>
          </Popover>
        </div>
      )}
    </PopupState>
  );
}

We have the PopupState coponent wirh the variant set to popover to display a popover.

Then inside it, we have a function that takes the popupState parameter, which has the open state of the popover.

We use the bindTrigger with it to return an object to toggle the popup in the button.

In the popover, we call bindPopover to bind the open prop to the popupState .

The rest of the code is the same as previous examples.

Conclusion

We can add popovers with the Popover component.

The material-ui-popup-state library can make managing the popover state easier.

Categories
Material UI

Material UI — More Tooltip Tricks and Typography

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 tooltips and add typography with Material UI.

Disabled Elements

If we want to show a tooltip on a disabled element, then we’ve to wrap the disabled element with a wrapper element.

For example, we can write:

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

export default function App() {
  return (
    <Tooltip title="disabled">
      <span>
        <Button disabled>Disabled Button</Button>
      </span>
    </Tooltip>
  );
}

We wrap the disabled button with a span so that we can see the tooltip when we hover over it.

Transitions

We can show various transitions when we show our tooltip.

For example, we can write:

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

export default function App() {
  return (
    <Tooltip
      TransitionComponent={Fade}
      TransitionProps={{ timeout: 600 }}
      title="tooltip"
    >
      <Button>Fade</Button>
    </Tooltip>
  );
}

to add a fade transition when we show or hide the tooltip.

Other effects include the zoom effect:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";
import Zoom from "@material-ui/core/Zoom";

export default function App() {
  return (
    <Tooltip
      TransitionComponent={Zoom}
      TransitionProps={{ timeout: 600 }}
      title="tooltip"
    >
      <Button>Fade</Button>
    </Tooltip>
  );
}

We set the TransitionProps to change the duration of the effect.

Showing and Hiding

We can also add a delay for showing and hiding the tooltip.

For example, we can write:

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

export default function App() {
  return (
    <Tooltip title="tooltip" enterDelay={500} leaveDelay={200}>
      <Button>button</Button>
    </Tooltip>
  );
}

We add a delay when we show the tooltip and when we hide it.

The numbers are in milliseconds.

Typography

We can change the font of our app with the Typography component.

The Roboto font isn’t automatically by Material UI.

To load it, we can add the following CSS:

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />

We can also install the fontsource-roboto package:

npm install fontsource-roboto

and write:

import 'fontsource-roboto';

Typography Component

We can add the Typography component to render various text styles.

For example, we can write:

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

export default function App() {
  return (
    <>
      <Typography variant="h1" component="h2" gutterBottom>
        h1.
      </Typography>
      <Typography variant="h2" gutterBottom>
        h2.
      </Typography>
      <Typography variant="h3" gutterBottom>
        h3.
      </Typography>
      <Typography variant="h4" gutterBottom>
        h4.
      </Typography>
      <Typography variant="h5" gutterBottom>
        h5.
      </Typography>
      <Typography variant="h6" gutterBottom>
        h6.
      </Typography>
    </>
  );
}

to render various headings with it.

The element we render is set in the variant prop.

gutterBottom adds some margins to the bottom.

There are also various body text styles we can render:

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

export default function App() {
  return (
    <>
      <Typography variant="subtitle1" gutterBottom>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos
        blanditiis tenetur
      </Typography>
      <Typography variant="subtitle2" gutterBottom>
        Morbi maximus maximus nisl, in tristique augue convallis vel. Praesent
        eget scelerisque ex, a sagittis libero.
      </Typography>
      <Typography variant="body1" gutterBottom>
        Vivamus ultrices nunc a est pulvinar sodales.
      </Typography>
      <Typography variant="body2" gutterBottom>
        Proin tincidunt nunc sed orci suscipit varius. Suspendisse volutpat
        interdum iaculis.
      </Typography>
    </>
  );
}

We set the variant to subtitle1 , subtitle2 , body1 or body2 to display various body text styles.

There’re also styles for buttons, captions, and overline text:

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

export default function App() {
  return (
    <>
      <Typography variant="button" display="block" gutterBottom>
        button text
      </Typography>
      <Typography variant="caption" display="block" gutterBottom>
        caption text
      </Typography>
      <Typography variant="overline" display="block" gutterBottom>
        overline text
      </Typography>
    </>
  );
}

Theme

We can use the typography key of a theme to style our text.

For example, we can write:

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

const useStyles = makeStyles(theme => ({
  root: {
    ...theme.typography.button,
    backgroundColor: theme.palette.background.paper,
    padding: theme.spacing(1)
  }
}));

export default function App() {
  const classes = useStyles();

  return <div className={classes.root}>some div</div>;
}

We added the theme.typography.button style to our styles so that we can apply it anywhere.

Conclusion

We can tooltips anywhere.

Typography components can be added to display text.

Categories
Material UI

Material UI — Modals

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

Modal

We can use the Modal component to display dialog boxes our way.

To create a simple one, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Modal from "@material-ui/core/Modal";

function getModalStyle() {
  const top = 50;
  const left = 50;

  return {
    top: `${top}%`,
    left: `${left}%`,
    transform: `translate(-${top}%, -${left}%)`
  };
}

const useStyles = makeStyles(theme => ({
  paper: {
    position: "absolute",
    width: 300,
    backgroundColor: theme.palette.background.paper,
    padding: 20
  }
}));

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

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

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

  const body = (
    <div style={modalStyle} className={classes.paper}>
      <h2>Modal</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
      <SimpleModal />
    </div>
  );

  return (
    <div>
      <button type="button" onClick={handleOpen}>
        Open Modal
      </button>
      <Modal open={open} onClose={handleClose}>
        {body}
      </Modal>
    </div>
  );
}

to create a simple modal.

We added some styles with the makeStyles component and the getModalStyle function.

We used makeStyles to set the background color and the positioning of the content.

We change the position to absolute so that it displays above everything else.

getModalStyle is used for positioning the modal.

Transitions

We can animate the modal with a transition component.

For instance, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Modal from "@material-ui/core/Modal";
import Backdrop from "@material-ui/core/Backdrop";
import Fade from "@material-ui/core/Fade";

const useStyles = makeStyles(theme => ({
  modal: {
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  },
  paper: {
    backgroundColor: theme.palette.background.paper,
    border: "2px solid gray",
    boxShadow: theme.shadows[5],
    padding: theme.spacing(2, 4, 3)
  }
}));

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

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

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

  return (
    <div>
      <button type="button" onClick={handleOpen}>
        open modal
      </button>
      <Modal
        className={classes.modal}
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500
        }}
      >
        <Fade in={open}>
          <div className={classes.paper}>
            <h2>fade modal</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
          </div>
        </Fade>
      </Modal>
    </div>
  );
}

to create a modal with a fade transition when we open or close it.

This is done with the Fade component.

The in prop specifies that we show the transition when open is true .

We then have what we want to display inside the modal.

We open the modal when we click the open modal button.

And we can click outside when we close it.

handleOpen sets the open state to true to open the modal.

hanleClose closes the modal to close the modal.

onClose is run when we click outside the modal.

BackdropComponent sets the component for the backdrop.

closeAfterTransition lets us wait until a nested transition is done before closing.

Also, we can use the react-spring package to add the transition:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Modal from "@material-ui/core/Modal";
import Backdrop from "@material-ui/core/Backdrop";
import { useSpring, animated } from "react-spring/web.cjs"; // web.cjs is required for IE 11 support

const useStyles = makeStyles(theme => ({
  modal: {
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  },
  paper: {
    backgroundColor: theme.palette.background.paper,
    border: "2px solid #000",
    boxShadow: theme.shadows[5],
    padding: theme.spacing(2, 4, 3)
  }
}));

const Fade = React.forwardRef(function Fade(props, ref) {
  const { in: open, children, onEnter, onExited, ...other } = props;
  const style = useSpring({
    from: { opacity: 0 },
    to: { opacity: open ? 1 : 0 },
    onStart: () => {
      if (open && onEnter) {
        onEnter();
      }
    },
    onRest: () => {
      if (!open && onExited) {
        onExited();
      }
    }
  });

  return (
    <animated.div ref={ref} style={style} {...other}>
      {children}
    </animated.div>
  );
});

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

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

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

  return (
    <div>
      <button type="button" onClick={handleOpen}>
        open menu
      </button>
      <Modal
        className={classes.modal}
        open={open}
        onClose={handleClose}
        closeAfterTransition
        BackdropComponent={Backdrop}
        BackdropProps={{
          timeout: 500
        }}
      >
        <Fade in={open}>
          <div className={classes.paper}>
            <h2>Spring modal</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
          </div>
        </Fade>
      </Modal>
    </div>
  );
}

We created the Fade component.

We use the useSpring hook provided by react-spring to add the animation.

We specify the transition style for the in transition in the friom property.

Also, we specify the transition style for the out transition with the to property.

onStart and onExited are functions that’s called when the transition starts and ends respectively to start and finish the transition.

Then we use Fade in the modal.

Conclusion

We can create modals with transitions by using components from Material UI or react-spring.