[Material-UI] Grid Componentでアイテムをレスポンシブに配置する

2021.07.20

こんにちは、CX事業本部 IoT事業部の若槻です。

Reactにマテリアルデザインを簡単に導入できるMaterial-UIでは、レスポンシブレイアウトグリッドを実装するための方法としてGridというComponentが提供されています。

このたびアンケート画面をMaterial-UIで実装する機会があったので、Grid Componentを使用してアイテム(回答ボタン)のレスポンシブな配置を試してみました。

環境

$ npm list --depth=0
...
├── @material-ui/core@4.12.2
├── react@17.0.2
├── typescript@4.2.4
...

実装

質問文のテキストと、回答の選択肢のボタンが配置されたアンケート画面を想定してみます。

src/App.tsx

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Choices } from "./Choice"; //選択肢となるテキストのリストをインポート

const useStyles = makeStyles((theme) => ({
  typography: {
    marginTop: theme.spacing(20),
    marginBottom: theme.spacing(10),
  },
  grid: {
    margin: "auto",
  },
  button: {
    marginTop: "auto",
    marginBottom: theme.spacing(5),
    width: "250px",
    height: "200px",
    fontSize: "30px",
    margin: theme.spacing(1),
  },
}));

const questionText = "今日の会社説明会はどうでしたか?";

const App: React.FC = () => {
  const classes = useStyles();

  return (
    <div>
      <Typography variant="h2" align="center" className={classes.typography}>
        <div>{questionText}</div>
      </Typography>
      <Grid container>
        {Choices.map((choice, index) => (
          <Grid item className={classes.grid}>
            <Button
              variant="contained"
              color="primary"
              className={classes.button}
            >
              {index + 1 + ". " + choice}
            </Button>
          </Grid>
        ))}
      </Grid>
    </div>
  );
};

export default App;

<Grid item></Grid>でレスポンシブレイアウトしたい一つ一つのアイテムを囲みます。今回は選択肢のボタンを対象とします。そして<Grid container></Grid>で各<Grid item>を囲み、レスポンスレイアウトの範囲を定義します。

<Grid item>margin: "auto"をスタイルとして当てることにより、マージンを自動化してレスポンシブを実現しています。

選択肢のテキストは外部モジュールCoice.tsxからリスト形式で取得しています。

ボタンの数を変えてみる

ボタンが2つ

src/Choice.tsx

export const Choices = ["満足", "不満"];

ボタンが3つ

src/Choice.tsx

export const Choices = ["満足", "普通", "不満"];

ボタンのサイズは固定なので、まだまだ横方向に配置できます。

ボタンが4つ

src/Choice.tsx

export const Choices = ["とても満足", "満足", "不満", "とても不満"];

少し窮屈になってきました。

ボタンが5つ

src/Choice.tsx

export const Choices = ["とても満足", "満足", "普通", "不満", "とても不満"];

いよいよ画面の横幅が限界です。

ボタンが6つ

src/Choice.tsx

export const Choices = ["とても満足", "満足", "普通", "不満", "とても不満", "その他"];

ついに画面の横幅にボタンが収まりきらなくなり、1列目からあふれたボタンがちゃんと2列目に配置されました。

画面のサイズを変えてみる

ボタンの並びが1列でなくなるブレークポイントは、ボタンのマージンが当てているスタイルmargin: theme.spacing(1)以下になった時です。ボタンを5つ並べた状態で画面幅を狭めてみます。

マージンぎりぎりの状態です。さらに狭めていきます。

2列となりました。これ以上5個1列の状態で画面幅が狭くなるとボタンの間隔が指定値以下となるためです。

おわりに

Grid Componentを使用してアイテム(回答ボタン)をレスポンシブな配置を試してみました。

画面に複数のアイテムをきれいに並べることが思いの外難しく、結構調べてしまいました。またmargin: "auto"を使わなくても出来る方法がある気がするので見つけたらまた共有します。

以上