styled-componentsで再利用可能なグリッドレイアウトを作る

# やること

Reactでコンポーネントを組み立ててUI作っていくのも楽なんですが、
styled-componentsでグリッドレイアウトのコンポーネントを作って、
それらを組み合わせて使うと非常に楽だということに気がつきました。

一度作ったやつを取っておけば再利用することもできますし:open_hands_tone2:

styled-componentsで書いたcssの記述も整理しやすいですし、おすすめです。
GitHub参考 => https://github.com/yutaro1204/styled-components-demo

# 前提

・styled-componentの使い方
・CSSでのdisplay: grid;の使い方

TypeScriptでReactのコンポーネントを書いていきますので、tsxを使います。

# 導入

まず気楽にcreate-react-appを使います。

```shell
$ npx create-react-app app-name --typescript
```

create-react-appが入っている場合はnpxは不要です。

styled-componentsをインストールします。

```shell
$ yarn add styled-components @types/styled-components
```

アプリを立ち上げます。デフォルトはhttp://localhost:3000となります。
```shell
$ yarn start
```

# コーディング

最終的にこんな感じの構造になります。

```
app-name/src
├── App.css
├── App.test.tsx
├── App.tsx
├── components
│   ├── Header.tsx // 追加
│   ├── Main.tsx // 追加
│   └── Sidepane.tsx // 追加
├── index.css
├── index.tsx
├── logo.svg
├── modules
│   └── styled-components.ts // 追加
├── react-app-env.d.ts
└── serviceWorker.ts
```

デフォルトで入っているsrcディレクトリ配下にmodulesディレクトリみたいなのを作って、
styled-components用のファイルを作成します。

```styled-components.ts
import style from 'styled-components'

const styledComponents = {
  LayoutStyles: {
    Body: {
      Grid: style.div`
        display: grid;
        grid-template-columns: 300px 1fr;
        grid-template-rows: 80px 1fr;
      `
    },
    Header: {
      Grid: style.div`
        grid-column: 1 / 3;
        grid-row: 1 / 2;
        display: grid;
        grid-template-columns: 40px 1fr 1fr 1fr 1fr 40px;
        background-color: #008080;
      `,
      // 状況によってgrid-columnの値を変化させる
      Item: style.div`
        grid-column: ${ ({theme}) => theme.column };
        max-height: 80px;
      `
    },
    Main: {
      Grid: style.div`
        grid-column: 2 / 3;
        grid-row: 2 / 3;
        display: grid;
        grid-template-columns: 100px 1fr 1fr 1fr 1fr 100px;
        height: calc(100vh - 80px);
        overflow-y: scroll;
        background-color: #ffe4c4;
      `,
      Item: style.div`
        grid-column: ${ ({theme}) => theme.column };
        grid-row: ${ ({theme}) => theme.row };
        height: 200px;
      `,
    },
    Sidepane: {
      Grid: style.div`
        grid-column: 1 / 2;
        grid-row: 2 / 3;
        height: calc(100vh - 80px);
        overflow-y: scroll;
        background-color: #696969;
      `,
      Item: style.div`
        width: 100%;
        height: 200px;
      `
    }
  },
  ComponentStyles: {
    // As you prefer...
  }
}

export default styledComponents
```

ここでは、LayoutStylesにBodyやHeaderなどのCSS情報をまとめたものと、
その他の細かいコンポーネントのCSS情報をComponentsStylesにまとめるようにしています（ここには何も書いていません）。

上のstyled-components.tsに従ってtsxを書き換えるとこんな感じになりました:point_down_tone2:

```App.tsx
import React from 'react'

import styledComponents from './modules/styled-components'

import Header from './components/Header'
import Sidepane from './components/Sidepane'
import Main from './components/Main'

const BodyGrid = styledComponents.LayoutStyles.Body.Grid

const App: React.FC = () => {
  return (
    <BodyGrid>
      <Header />
      <Sidepane />
      <Main />
    </BodyGrid>
  )
}

export default App
```

んで、参考までにこのHeaderコンポーネントとなっているHeader.tsxはこんな感じに:point_down_tone2:

```Header.tsx
import React from 'react'
import styledComponents from '../modules/styled-components'

const HeaderGrid = styledComponents.LayoutStyles.Header.Grid
const HeaderItem = styledComponents.LayoutStyles.Header.Item

const Header: React.FC = () => {
  return (
    <HeaderGrid>
      <HeaderItem theme={{ column: '2 / 3' }}>
        <p>Grid1</p>
      </HeaderItem>
      <HeaderItem theme={{ column: '3 / 4' }}>
        <p>Grid2</p>
      </HeaderItem>
      <HeaderItem theme={{ column: '4 / 5' }}>
        <p>Grid3</p>
      </HeaderItem>
      <HeaderItem theme={{ column: '5 / 6' }}>
        <p>Grid4</p>
      </HeaderItem>
    </HeaderGrid>
  )
}

export default Header
```

おんなじ要領でHeader、Sidepane、Mainを組み合わせると、こんな感じになります。

<img width="1440" alt="Screenshot 0031-06-02 at 9.46.36 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/a77eadbc-457a-28d3-69ca-ffdb03eb7f6b.png">

ちょっと地味ですが、ここから色々装飾すればそれなりになるかと:information_desk_person:

styled-componentsとreactDnDをつかってなんか作りたい今日この頃。。。