styled-componentsとAtomic Design @yukilulu0229

https://styled-components.com/

## インストール

```
npm install styled-components
```

https://www.npmjs.com/package/styled-components

## atom

```atom/button/PrimaryButton.jsx
import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";

const PrimaryButton = (props) => {
  const {children} = props
  return (
    <>
      <SButton>{children}</SButton>
    </>
  );
};

const SButton = styled.button`
    background-color: #40514e;
    color: #fff;
    padding: 6px 24px;
    border: none;
    outline: none;
    border-radius: 9999px;
    &:hover {
        cursor: pointer;
        opacity: 0.8;
    }
`

export default PrimaryButton;

```

ボタン名をpropsで受け取る

```AtomicDesign.jsx
import React from 'react';
import PrimaryButton from "./atom/button/PrimaryButton.jsx";
import SecondaryButton from "./atom/button/SecondaryButton.jsx";
import SearchInput from "./molecules/SearchInput.jsx";

const AtomicDesign = () => {
  return (
    <div>
      <p>AtomicDesign</p>

      <PrimaryButton>テスト</PrimaryButton>
      <SecondaryButton>検索</SecondaryButton>
    </div>
  );
};

export default AtomicDesign;

```

ボタン名をpropsで渡す

### 共通の部分を抜き出す

```atom/button/BaseButton.jsx
import styled from "styled-components";

const BaseButton = styled.button`
    color: #fff;
    padding: 6px 24px;
    border: none;
    outline: none;
    border-radius: 9999px;
    &:hover {
        cursor: pointer;
        opacity: 0.8;
    }
`

export default BaseButton;

```

共通部分

```atom/button/PrimaryButton.jsx

import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";

const PrimaryButton = (props) => {
  const {children} = props
  return (
    <>
      <SButton>{children}</SButton>
    </>
  );
};

const SButton = styled(BaseButton)`
    background-color: #40514e;
`

export default PrimaryButton;

```

styled(BaseButton)を使い
共通部分を呼び出す
BaseButtonが上書きされる


## molecules

```molecules/SearchInput.jsx

import React from 'react';
import PrimaryButton from "../atom/button/PrimaryButton.jsx";
import Input from "../atom/input/Input.jsx";
import styled from "styled-components";

const SearchInput = () => {
  return (
    <SContainer>
      <Input placeholder="検索条件を表示" />

      <SButtonWrapper>
        <PrimaryButton>検索</PrimaryButton>
      </SButtonWrapper>
    </SContainer>
  );
};

const SContainer = styled.div`
    display: flex;
    align-items: center;
`

const SButtonWrapper = styled.div`
    padding-left: 8px;
`

export default SearchInput;

```

atomを読み込んで組み合わせる


```Input.jsx

import React from 'react';
import styled from "styled-components";

const Input = (props) => {
  const {placeholder = ""} = props
  return (
    <>
      <SInput type="text" placeholder={placeholder} />
    </>
  );
};

const SInput = styled.input`
    padding: 8px 16px;
    border: solid 1px #ddd;
    border-radius: 9999px;
    outline: none;
`

export default Input;

```

placeholderでpropsを受け取る

```PrimaryButton.jsx

import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";

const PrimaryButton = (props) => {
  const {children} = props
  return (
    <>
      <SButton>{children}</SButton>
    </>
  );
};

const SButton = styled(BaseButton)`
    background-color: #40514e;
`

export default PrimaryButton;

```

## organisms

上位でもatom(Card)で囲む場合もある? ここはルールがよくわからな

```AtomicDesigh.jsx

import React from 'react';
import PrimaryButton from "./atom/button/PrimaryButton.jsx";
import SecondaryButton from "./atom/button/SecondaryButton.jsx";
import SearchInput from "./molecules/SearchInput.jsx";
import UserCard from "./organisms/user/UserCard.jsx";

const AtomicDesign = () => {
  const user = {
    name: "yukilulu0229",
    image: "https://picsum.photos/160",
    email: "apple@fluits.com",
    tel: "01-2345-6789",
    company: {
      name: "company web"
    },
    website: "xxx.com"

  }
  return (
    <div>
      <UserCard user={user} />
    </div>
  );
};

export default AtomicDesign;

```

user情報を渡す

```organisms/user/UserCard.jsx

import React from 'react';
import styled from "styled-components";
import Card from "../../atom/card/Card.jsx";
import UserIconWithName from "../../molecules/user/UserIconWithName.jsx";

const UserCard = (props) => {
  const {user} = props
  return (
    <Card>
      <UserIconWithName image={user.image} name={user.name} />

      <SDl>
        <dt>メール</dt>
        <dd>{user.email}</dd>

        <dt>tel</dt>
        <dd>{user.tel}</dd>

        <dt>会社名</dt>
        <dd>{user.company.name}</dd>

        <dt>web</dt>
        <dd>{user.website}</dd>
      </SDl>
    </Card>
  );
};

const SDl = styled.dl`
    text-align: left;
    margin-bottom: 0;
    
    dt {
        float: left;
    }
    
    dd {
        padding-left: 32px;
        padding-bottom: 8px;
        overflow-wrap: break-word;
    }
    
`

export default UserCard;

```

user情報を受け取る
UserIconWithNameにpropsを渡す
Cardの中をchildrenで渡す

```molecules/user/UserIconWithName.jsx

import React from 'react';
import styled from "styled-components";

const UserIconWithName = (props) => {
  const {image, name} = props
  return (
    <SContainer>
      <SImage src={image} alt="プロフィール" />
      <SName>{name}</SName>
    </SContainer>
  );
};

const SContainer = styled.div`
    text-align: center;
`
const SImage = styled.img`
    border-radius: 50%;
`
const SName = styled.p`
    font-size: 18px;
    font-weight: bold;
    margin: 0;
    color: #303030;
`
export default UserIconWithName;


```
imageとnameのpropsを受け取る

```atom/card/Card.jsx
import React from 'react';
import styled from "styled-components";

const Card = (props) => {
  const {children} = props
  return (
    <SCard>{children}</SCard>
  );
};

const SCard = styled.div`
    background-color: #d7d1d1;
    box-shadow: #ddd 0 0 4px 2px;
    border-radius: 8px;
    padding: 16px;
`

export default Card;

```

UserCardからchildrenを受け取る

## templates

```atomicDesign.jsx
import React from 'react';
import DefaultLayout from "./templates/DefaultLayout.jsx";

const AtomicDesign = () => {
  return (
    <DefaultLayout>
      <p>AtomicDesign</p>
    </DefaultLayout>
  );
};

export default AtomicDesign;

```

DefaultLayoutにchildrenを渡す

```templates/DefaultLayout.jsx
import React from 'react';
import Footer from "../atoms/layout/Footer.jsx";
import Header from "../atoms/layout/Header.jsx";
import styled from "styled-components";


const DefaultLayout = (props) => {
  const {children} = props
  return (
    <>
      <SDefaultLayout>
        <Header />
        {children}
        <Footer />
      </SDefaultLayout>
    </>
  );
};


const SDefaultLayout = styled.div`
    min-height: 100vh;
`
export default DefaultLayout;

```

HeaderとFooterを読み込む
atomicDesignで渡されたchildrenを受け取る
そしてHeaderとFooterにchildrenを入れている


```atom/Layout/Header.jsx
import React from 'react';
import {Link} from "react-router-dom";
import styled from "styled-components";

const Header = () => {
  return (
    <SHeader>
      <SLink to="/AtomicDesign">AtomicDesign</SLink>
      <SLink to="/AtomicDesign">Users</SLink>
    </SHeader>
  );
};

const SHeader = styled.header`
    background-color: #11999e;
    color: #fff;
    text-align: center;
    padding: 8px 0;
`
const SLink = styled(Link)`
    margin: 0 8px;
    color: #fff;
`

export default Header;

```

これがatomsの中でよいのかは疑問が残っている

```atoms/layout/footer.jsx
import React from 'react';
import {Link} from "react-router-dom";
import styled from "styled-components";

const Footer = () => {
  return (
    <SFooter>
      <p>&copy; company web</p>
    </SFooter>
  );
};

const SFooter = styled.footer`
    background-color: #11999e;
    color: #fff;
    text-align: center;
    padding: 8px 0;
    position: fixed;
    bottom: 0;
    width: 100%;
`
export default Footer;

```

これがatomsの中でよいのかは疑問が残っている

## pages

今回の話はUsersのほう(Usersは共通の部品がないのでtemplateを使っていない)

```router/Router.jsx
import React from 'react';
import { Route, Routes } from "react-router-dom";
import Home from "../components/Home.jsx";
import Page2 from "../components/Page2.jsx";
import Page3 from "../components/Page3.jsx";
import Top from "../components/pages/Top.jsx";
import Users from "../components/pages/Users.jsx";
import DefaultLayout from "../components/templates/DefaultLayout.jsx";
import Page404 from "../Page404.jsx";
import { page2Routes } from "./Page2Routes.jsx";
import { page3Routes } from "./Page3Routes.jsx";

const Router = () => {
  return (
    <>
      <Routes path="/AtomicDesign/*" >
        <Route path="/" element={<DefaultLayout />} >
          <Route index={true} element={<Top/>} />
        </Route>
        <Route path="Users/*" element={<Users />} />
      </Routes>

    </>
  );
};

export default Router;

```

routerの設定でlayoutを選択している

```pages/Users.jsx
import React from 'react';
import styled from "styled-components";
import Header from "../atoms/layout/Header.jsx";
import SearchInput from "../molecules/SearchInput.jsx";
import UserCard from "../organisms/user/UserCard.jsx";

const Users = () => {
  const users = [...Array(10).keys()].map((value) => {
    return {
      id: value,
      name: `yukilulu0229 + ${value}`,
      image: "https://picsum.photos/160",
      email: "apple@fluits.com",
      tel: "01-2345-6789",
      company: {
        name: "company web"
      },
      website: "xxx.com"
    }
  })
  return (
    <>
      <SContainer>

        <Header />

        <h2>User Page</h2>

        <SearchInput />


        <SUserArea>
          {users.map((user) => (
            <UserCard  key={user.id} user={user} />
          ))}
          </SUserArea>
      </SContainer>
    </>
  );
};

const SContainer = styled.div`
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
`

const SUserArea = styled.div`
    padding-top: 40px;
    width: calc(100% - 48px);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    grid-gap: 20px;
`

export default Users;



```