React UI library の antd について (2) - Layout

[React UI library の antd について (1) - Button](https://qiita.com/sand/items/35422a669e6021cccb4f)
[React UI library の antd について (2) - Layout](https://qiita.com/sand/items/dd420202e6c7daaea667)
[React UI library の antd について (3) - redux-form](https://qiita.com/sand/items/a0cf3fa3a1a17741c06b)


---

前回は antd のインストールとButton componentを見ました。簡単には使えましたが、BootstrapやMaterial-UIとの差があまりわかりませんでした。
[React UI library の antd について (1) - Button](https://qiita.com/sand/items/35422a669e6021cccb4f)

今回はLayoutを見ますが、他のUIライブラリと比べると、コードが論理的にスッキリと書けるような気がします。



#１．Grid

[Antd Grid システム](https://ant.design/components/grid/)

antd の Grid システムは以下のルールに従います。

* 行を **row** で表し、row の中に複数の列 **col** を配置します。
* colの中にコンテンツを配置します。row 直下の要素は col 以外は置けません。
* 各rowの中に**24**個までのcolを配置できます。24個を超えた場合は、溢れたcolは改行されます。
* Grid システムは**Flex layout**をサポートしています。


## １－１．シンプルな例

```JSX:src/App.js
import React, { Component } from 'react';
import { Row, Col } from 'antd';
import './App.css';

const style1 = { background: "#66CDAA", padding: "20px", fontWeight: "bold" }
const style2 = { background: "#AFEEEE", padding: "20px" , fontWeight: "bold"}

class App extends Component {

  render() {
    return (
      <div style={{ margin: '10px'}}>
        <Row style={{ margin: '10px'}}>
          <Col span={12} style={style1}>col-12</Col>
          <Col span={12} style={style2}>col-12</Col>
        </Row>
        <Row style={{ margin: '10px'}}>
          <Col span={8} style={style1}>col-8</Col>
          <Col span={8} style={style2}>col-8</Col>
          <Col span={8} style={style1}>col-8</Col>
        </Row>
        <Row style={{ margin: '10px'}}>
          <Col span={6} style={style1}>col-6</Col>
          <Col span={6} style={style2}>col-6</Col>
          <Col span={6} style={style1}>col-6</Col>
          <Col span={6} style={style2}>col-6</Col>
        </Row>
      </div>
    );
  }
}

export default App;
```


以下が実行例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/3b05e016-1d18-d57d-5d08-8d464003b084.png)


## １－２．Flex Layout

**Flex Layout**を使うには、Rowで**type="flex"**を指定し、**justify**でLayout ( start end center space-around space-between ) を指定します。

```JSX:src/App.js
import React, { Component } from 'react';
import { Row, Col } from 'antd';
import './App.css';

const style1 = { background: "#66CDAA", padding: "20px", fontWeight: "bold" }
const style2 = { background: "#AFEEEE", padding: "20px" , fontWeight: "bold"}

class App extends Component {

  render() {
    return (
      <div style={{ margin: '10px'}}>
        <p>sub-element align left</p>
        <Row type="flex" justify="start" style={{ margin: '10px'}}>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
        </Row>

        <p>sub-element align center</p>
        <Row type="flex" justify="center" style={{ margin: '10px'}}>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
        </Row>

        <p>sub-element align right</p>
        <Row type="flex" justify="end" style={{ margin: '10px'}}>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
        </Row>

        <p>sub-element monospaced arrangement</p>
        <Row type="flex" justify="space-between" style={{ margin: '10px'}}>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
        </Row>

        <p>sub-element align full</p>
        <Row type="flex" justify="space-around" style={{ margin: '10px'}}>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
          <Col span={4} style={style1}>col-4</Col>
          <Col span={4} style={style2}>col-4</Col>
        </Row>
      </div>
    );
  }
}

export default App;
```


以下のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/eeb9a034-8a36-be15-022b-3a320e5e02dd.png)



##１－３． レスポンシブデザイン

レスポンシブデザインはBootstrap流の指定で実現できます。以下のコードと実行結果を参照してください。


```JSX:src/App.js
import React, { Component } from 'react';
import { Row, Col } from 'antd';
import './App.css';

const style1 = { background: "#66CDAA", padding: "20px", fontWeight: "bold" }
const style2 = { background: "#AFEEEE", padding: "20px" , fontWeight: "bold"}

class App extends Component {

  render() {
    return (
      <Row style={{ margin: '10px'}}>
        <Col xs={2} sm={4} md={6} lg={8} xl={10} style={style1}>
          Col
        </Col>
        <Col xs={20} sm={16} md={12} lg={8} xl={4} style={style2}>
          Col
        </Col>
        <Col xs={2} sm={4} md={6} lg={8} xl={10} style={style1}>
          Col
        </Col>
      </Row>
    );
  }
}

export default App;
```

以下ブラウザのサイズごとのレイアウトです。レスポンシブデザインが実現できていることが確認できます。

画面サイズ=xl
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/ad5d807a-8e17-8543-3eca-b8d68bafdc04.png)

画面サイズ=lg
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/b5d7b9a1-53f0-7bf0-2fce-680f342403c9.png)


画面サイズ=md
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/7bdbd204-16bf-57dc-b4ae-583cd128b3fd.png)


画面サイズ=sm
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/4363eb2c-b738-82de-c623-9a59b280c783.png)


画面サイズ=xs
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/fd16f9f1-a0f9-653d-cbbe-5b8e6eaab1f8.png)



##１－４． 簡単なplayground - column数とgutter.

**Slider**で、**gutter**とcolumn数を変化させ、レイアウトが動的に変わっていくのを確認します。

Rowの属性である**gutter**はgrid間のスペースを指定するものです。Col間のスペースというよりは、Colのコンテンツ（以下の例ではdiv）間のスペースを取ってくれます。セレクタ [class~='ant-col'] > div で指定されるコンテンツ間のスペースです。 



```CSS:src/index.css
[class~='ant-col'] {
  background: transparent;
  border: 0;
}
[class~='ant-col'] > div {
  background: #00a0e9;
  height: 120px;
  line-height: 120px;
  font-size: 13px;
}
pre {
  background: #f9f9f9;
  border-radius: 6px;
  font-size: 13px;
  padding: 8px 16px;
}
```

[Slide component](https://ant.design/components/slider/)で**gutter**とcolumn数を変化させ、RowとColを定義・描画していきます。



```JSX:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Row, Col, Slider } from 'antd';

class App extends React.Component {
  gutters = {};

  colCounts = {};

  constructor() {
    super();
    this.state = {
      gutterKey: 1,
      colCountKey: 2,
    };
    [8, 16, 24, 32, 40, 48].forEach((value, i) => {
      this.gutters[i] = value;
    });
    [2, 3, 4, 6, 8, 12].forEach((value, i) => {
      this.colCounts[i] = value;
    });
  }

  onGutterChange = gutterKey => {
    this.setState({ gutterKey });
  };

  onColCountChange = colCountKey => {
    this.setState({ colCountKey });
  };

  render() {
    const { gutterKey, colCountKey } = this.state;
    const cols = [];
    const colCount = this.colCounts[colCountKey];
    let colCode = '';
    for (let i = 0; i < colCount; i++) {
      cols.push(
        <Col key={i.toString()} span={24 / colCount}>
          <div>Column</div>
        </Col>,
      );
      colCode += `  <Col span={${24 / colCount}} />\n`;
    }
    return (
      <div>
        <div style={{ marginBottom: 16 }}>
          <span style={{ marginRight: 6 }}>Gutter (px): </span>
          <div style={{ width: '50%' }}>
            <Slider
              min={0}
              max={Object.keys(this.gutters).length - 1}
              value={gutterKey}
              onChange={this.onGutterChange}
              marks={this.gutters}
              step={null}
            />
          </div>
          <span style={{ marginRight: 6 }}>Column Count:</span>
          <div style={{ width: '50%' }}>
            <Slider
              min={0}
              max={Object.keys(this.colCounts).length - 1}
              value={colCountKey}
              onChange={this.onColCountChange}
              marks={this.colCounts}
              step={null}
            />
          </div>
        </div>
        <Row gutter={this.gutters[gutterKey]}>{cols}</Row>
        <pre>{`<Row gutter={${this.gutters[gutterKey]}}>\n${colCode}</Row>`}</pre>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
```

以下は実行結果です。

Gutter=16, column数=4の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/ee619ec3-3c7e-ccfa-b7e1-303144b35b3c.png)

Gutter=48, column数=4の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/bc4aa772-491e-d8c8-b732-a7cdb3e9b372.png)


Gutter=48, column数=8の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/3fa93bee-0fd0-49b3-d8ad-4fb8e06483d9.png)


# ２．Layout

[Layout Component](https://ant.design/components/layout/)はページ全体のLayoutを記述します。

Layout Componentの概要

* **Layout**: Header、 Sider、 Content、 Footer または Layout自身のwrpper
* **Header**: トップ Layout
* **Sider**: sidebar 
* **Content**: content layout
* **Footer**: ボトム Layout



##２－１．シンプルな例

Layoutのシンプルな例です。

```JSX:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Layout } from 'antd';

const { Header, Footer, Sider, Content } = Layout;

ReactDOM.render(
  <div>
    <Layout>
      <Sider>Sider</Sider>
      <Layout>
        <Header>Header</Header>
        <Content>Content</Content>
        <Footer>Footer</Footer>
      </Layout>
    </Layout>
  </div>,
  document.getElementById('container'),
);
```

cssファイルです。

```CSS:src/index.css
.ant-layout-header,
.ant-layout-footer {
  background: #7dbcea;
  color: #fff;
}
.ant-layout-footer {
  line-height: 1.5;
}
.ant-layout-sider {
  background: #3ba0e9;
  color: #fff;
  line-height: 120px;
}
.ant-layout-content {
  background: rgba(16, 142, 233, 1);
  color: #fff;
  min-height: 120px;
  line-height: 120px;
}
```

枠組みだけですが、ページ全体がレイアウトされています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/1a05d291-a1e3-73f6-fba0-8ab4796ed3b5.png)



##２－２． レスポンシブデザイン


**Sider**はbreakpointを設定することで、レスポンシブデザインをサポートするようになります。windowの横幅がbreakpoint以下になったら、折りたたまれます。

Menu componentが簡単に使えて、Icon componentで様々なアイコンを指定できるのも便利です。他のUIライブラリに比べて、全体的に論理的にスッキリと書けるのではないでしょうか？


```JSX:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Layout, Menu, Icon } from 'antd';

const { Header, Content, Footer, Sider } = Layout;

ReactDOM.render(
  <Layout>
    <Sider
      breakpoint="lg"
      collapsedWidth="0"
      onBreakpoint={broken => {
        console.log(broken);
      }}
      onCollapse={(collapsed, type) => {
        console.log(collapsed, type);
      }}
    >
      <div className="logo" />
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['4']}>
        <Menu.Item key="1">
          <Icon type="user" />
          <span className="nav-text">nav 1</span>
        </Menu.Item>
        <Menu.Item key="2">
          <Icon type="video-camera" />
          <span className="nav-text">nav 2</span>
        </Menu.Item>
        <Menu.Item key="3">
          <Icon type="upload" />
          <span className="nav-text">nav 3</span>
        </Menu.Item>
        <Menu.Item key="4">
          <Icon type="user" />
          <span className="nav-text">nav 4</span>
        </Menu.Item>
      </Menu>
    </Sider>
    <Layout>
      <Header style={{ background: '#fff', padding: 0 }} />
      <Content style={{ margin: '24px 16px 0' }}>
        <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>content</div>
      </Content>
      <Footer style={{ textAlign: 'center' }}>Ant Design c2018 Created by Ant UED</Footer>
    </Layout>
  </Layout>,
  document.getElementById('root'),
);
```

小さいですがCSSです。

```CSS:src/index.css
.logo {
  height: 32px;
  background: rgba(255, 255, 255, 0.2);
  margin: 16px;
}
```



初期画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/85a5629b-e5d2-ea3b-c8a7-402d11abc317.png)



ブラウザの横幅を縮めると、メニューが折りたたまれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/8ee38a43-8175-0ec6-76ca-f2fd97c715dd.png)


メニューを選択すると、横から飛び出す感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/01e8344b-4fa0-febd-8390-e9c597b58d75.png)



今回は以上です