lamechang-dev

Webフロントエンドエンジニア lamechangのブログ。

【material-ui】Gridコンポーネントのブレークポイント propsにbooleanを渡した際の挙動を知る

material-uiのgridコンポーネントとprops

material-uiをデザインシステムとして利用した際に、レスポンシブデザインの対応方法としてgridレイアウトにブレークポイントを設定することは多いと思います。以下のような設定値を渡すことができます。

xs 全てのサイズに対応
sm(small): 600dp〜
md(medium): 960dp〜
lg(large): 1280dp〜
xl(xlarge): 1920dp〜

bootstrapなどの経験があれば見慣れた定義だとは思います(実はブレイクポイントの値が違います)。モバイルファーストで考えてレイアウトしていき、必要に応じてmd、lg等を追加して大きいサイズでのスクリーンを考慮した実装を進めることになりそうです。

公式docの grid コンポーネント を見れば、container item propsの使い方やspacing, そしてbreakpointを設定した際の挙動があらかた追えるのではないでしょうか。

docのサンプルには、codesandboxへのリンクが実はついているので、コードをいじって挙動を確かめたい時に非常に便利です。自分もこれで諸々実験をすることでGridレイアウトの勉強をしました。

f:id:lamechang_dev:20210615190011p:plain

Grid コンポーネントのコードサンプルに登場する、booleanで定義されたブレークポイント

複雑なGrid という例に登場する、Gridが入れ子になっており多少複雑なレイアウトのコードを読んでいる際に、ブレークポイントのpropsに対してbooleanを指定する記述がを見つけました。以下の通り。 f:id:lamechang_dev:20210615190431p:plain

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import ButtonBase from '@material-ui/core/ButtonBase';

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
  },
  paper: {
    padding: theme.spacing(2),
    margin: 'auto',
    maxWidth: 500,
  },
  image: {
    width: 128,
    height: 128,
  },
  img: {
    margin: 'auto',
    display: 'block',
    maxWidth: '100%',
    maxHeight: '100%',
  },
}));

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

  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <Grid container spacing={2}>
          <Grid item>
            <ButtonBase className={classes.image}>
              <img className={classes.img} alt="complex" src="/static/images/grid/complex.jpg" />
            </ButtonBase>
          </Grid>
          <Grid item xs={12} sm container>
            <Grid item xs container direction="column" spacing={2}>
              <Grid item xs>
                <Typography gutterBottom variant="subtitle1">
                  Standard license
                </Typography>
                <Typography variant="body2" gutterBottom>
                  Full resolution 1920x1080 • JPEG
                </Typography>
                <Typography variant="body2" color="textSecondary">
                  ID: 1030114
                </Typography>
              </Grid>
              <Grid item>
                <Typography variant="body2" style={{ cursor: 'pointer' }}>
                  Remove
                </Typography>
              </Grid>
            </Grid>
            <Grid item>
              <Typography variant="subtitle1">$19.00</Typography>
            </Grid>
          </Grid>
        </Grid>
      </Paper>
    </div>
  );
}

これ、例えばsm = {6}のように指定すると、表示幅がsm(600px)を超えると6列分の範囲を占めるようにレイアウトされるみたいな挙動については理解していましたが、上記のコードにもある通り、値としてboolean値を渡した際の挙動がイマイチわかりません。

gridのapi仕様 を見ると確かにブレークポイントにbooleanを渡せることは記載がありますが、その挙動については特に言及されていません。なのでコードを読んでみました。

  GRID_SIZES.forEach((size) => {
    const key = `grid-${breakpoint}-${size}`;

    if (size === true) {
      // For the auto layouting
      styles[key] = {
        flexBasis: 0,
        flexGrow: 1,
        maxWidth: '100%',
      };
      return;
    }

    if (size === 'auto') {
      styles[key] = {
        flexBasis: 'auto',
        flexGrow: 0,
        maxWidth: 'none',
      };
      return;
    }

    // Keep 7 significant numbers.
    const width = `${Math.round((size / 12) * 10e7) / 10e5}%`;

    // Close to the bootstrap implementation:
    // https://github.com/twbs/bootstrap/blob/8fccaa2439e97ec72a4b7dc42ccc1f649790adb0/scss/mixins/_grid.scss#L41
    styles[key] = {
      flexBasis: width,
      flexGrow: 0,
      maxWidth: width,
    };
  });

どうやら、sizetrueであった場合は、以下のプロパティが適用されるみたいですね。表示幅が指定した幅を超えると、余ったスペースをflexGrow記載の比率で伸びるようなレイアウトになる、と言うことです。上記の複雑なGridコンポーネントの例では、カードの文字部分について、幅がsm以下に相当する場合は画像に対して1つ下のグリッド行に移動し、またsm以上になったタイミングで画像と同じ行において、横並びに余ったスペースを埋め尽くすようなレイアウトになっていることがわかりました。

flexBasis: 0,
flexGrow: 1,
maxWidth: '100%',

また、falseが適用された場合は何も適用されないようです。