18_styled-components
FrontEnd/React

18_styled-components

728x90

CSS om JS 관련 라이브러리로, JS안에 CSS를 작성하는 요령이다.

 

 

 

Tagged Template Literal

 

const name 'react';
const message = `hello ${name}`;

console.log(text);

위와같이 사용하는 문법이다. ` `사이로 온다.

 

const object = {a : 1};
const text = `${object}`
console.log (text) // "[object Object]"

하지만 안에 객체를 넣으면 위처럼 안의 내용이 제대로 표시되지 않는다.

 

const fn = () => true
const msg = `${fn}`;
console.log(msg);
// "() => true"

마찬가지로 함수를 넣어도 함수의 문자열 형태를 그대로 리턴하게 된다.

 

 

const red = '빨간색';
const blue = '파란색';
function favoriteColors(texts, ...values) {
  console.log(texts);
  console.log(values);
}
favoriteColors`제가 좋아하는 색은 ${red}과 ${blue}입니다.`
//(3) ['제가 좋아하는 색은 ', '과 ', '입니다.', raw: Array(3)]
//(2) ['빨간색', '파란색']

Template Literal을 사용하면 위같은 예제를 만들 수 있다.

 

즉, text와 value가 분리가 되서 위처럼 배열로 나타나게 된다.

 

 

const red = '빨간색';
const blue = '파란색';
function favoriteColors(texts, ...values) {
   return texts.reduce((result, text, i) => `${result}${text}${values[i] ? `<b>${values[i]}</b>` : ''}`, '');
}
favoriteColors`제가 좋아하는 색은 ${red}과 ${blue}입니다.`
// 제가 좋아하는 색은 <b>빨간색</b>과 <b>파란색</b>입니다.

이를 좀 더 활용하면 위처럼 응용할 수 있다.

 

이러한 속성을 활용하면 styled component를 이용하여 props를 읽기 위해 사용될 수 있다.

 

좀더 자세히 알아보자.

 

function sample(texts, ...fns) {
  const mockProps = {
    title: '안녕하세요',
    body: '내용은 내용내용 입니다.'
  };
  return texts.reduce((result, text, i) => `${result}${text}${fns[i] ? fns[i](mockProps) : ''}`, '');
}
sample`
  제목: ${props => props.title}
  내용: ${props => props.body}
`
/*
"
  제목: 안녕하세요
  내용: 내용은 내용내용 입니다.
"
*/

즉, sample함수를 실행하면 texts에는 제목,내용이 ...fns에는 ${props => props.tilte } 함수가 들어가게 된다.

 

그 이후 reduce문법을 사용해서 fns[i]가 있을때 인자로 넣어주는 값을 출력하게 하는 코딩이다.

 

 

자 이제 실제로 적용해보자.

 

 yarn add styled-components

우선 리액트 프로젝트를 만들고 styled-components를 설치해 준다.

 

 

import React from 'react'
import './App.css';
import styled from 'styled-components'

const Circle = styled.div`
  width : 5rem;
  height : 5rem;
  background : ${props => props.color};
  border-radius : 50%;

  
`;


function App() {
  return (
    <Circle color = "blue"/>
  );
}


export default App;

위처럼 color값을 props로 받아와서 style을 JS안에서 실행시킬 수 있다.

import React from 'react'
import './App.css';
import styled, { css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동

const Circle = styled.div`
  width : 5rem;
  height : 5rem;
  background : ${props => props.color};
  border-radius : 50%;

  ${props => props.huge &&
    //``안에 ``사용 가능 (styled-components로)
    css`   
    width : 10rem;
    height : 10rem;
    `  }
  `;
  
function App() {
  return (
    <>
      <Circle color = "blue" huge/>
      <Circle color = "blue"/>
    </>
  );
}


export default App;

huge를 넣어서 그 값에따라 css를 변경하려면 위처럼 하면 된다. 이때 css ` `를 사용해야지 일반 tempelate Literal로 작동되지 않는다.

 

 

 

 

재사용성이 높은 버튼 만들기

 

 

이러한 sytled-component를 사용하여 버튼을 만들어보자 원리는 위에 설명했던것과 똑같다.

 

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

const StyledButton = styled.button`
    display : inline-flex;
    outline : none;
    border : none;
    border-radius : 4px;
    color : white;
    font-weight : bold;
    cursor : pointer;
    padding-left : 1rem;
    padding-right : 1rem;

    height : 2.25rem;
    font-size : 1rem;

    background : #228be6;
    &:hover {
        background : #228be6;
    }
    &:active {
        background : #1c7ed6
    }

    & + &{
        margin-left : 1rem;
    }

`;


function Button({ children, ...rest}) {
    return (
        <StyledButton {...rest}>{children}</StyledButton>
    );
    
}


export default Button;

코드를 작성하고 App.js에 연동만 시켜주면 된다.

 

import React from 'react'
import './App.css';
import styled, { css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동
import Button from './components/Button';

const AppBlock = styled.div`

width : 512px;
margin : 0 auto;
margin-top : 4rem;
border : 1px solid black;
padding : 1rem;
`;
  


function App() {
  return (
    <>
      <AppBlock>
        <Button>BUTTON</Button>
      </AppBlock>
    </>
  );
}


export default App;

 

 

Polished 스타일 유틸 함수

 

위 함수에 있는 여러 내장 함수들을 이용하면 편하게 버튼들을 관리할 수 있다.

 

 

yarn add polished

모듈을 깔고 사용하면 된다.

 

 

 

//App.js

// ...
const palette = {
  blue : '#228be6',
  gray : '#496057',
  pink : '#f06595'
}

function App() {
  return (
    <ThemeProvider theme={{
      palette
    }}>
      <AppBlock>
        <Button>BUTTON</Button>
      </AppBlock>
    </ThemeProvider>
  );
}


export default App;

ThemeProvider을 불러주고 위처럼 블럭을 감싸주고,

 

//Button.js

background : ${props => props.theme.palette.blue};
    &:hover {
        background : ${props => lighten(0.1, props.theme.palette.blue)}
    }
    &:active {
        background : ${props => darken(0.1, props.theme.palette.blue)}
    }

위 부분처럼 바꿔주어도 동일작동을 하게 된다. 즉, props의 color을 가져와서 사용할 수 있다.

 

 

 

   ${props =>{
        const color = props.theme.palette.blue;
        return css`
            background : ${color};
            &:hover {
                background : ${lighten(0.1, color)}
            }
            &:active {
                background : ${darken(0.1, color)}
            }
        `;
    }}

props로 묶으면 굳이 색상마다 props.them.palette.blue 이렇게 쓰지 않아도 된다.

 

 

 ${({theme,color}) =>{
        const selected = theme.palette[color];
        return css`
            background : ${selected};
            &:hover {
                background : ${lighten(0.1, selected)}
            }
            &:active {
                background : ${darken(0.1, selected)}
            }
        `;
    }}

비구조화 할당을 통하여 보다 간단하게 만들 수도 있다.

 

 

더보기
import React from 'react';
import styled, {css}from 'styled-components';
import {darken,lighten} from 'polished';

const colorStyles = css`
    ${({theme,color}) =>{
        const selected = theme.palette[color];
        return css`
            background : ${selected};
            &:hover {
                background : ${lighten(0.1, selected)}
            }
            &:active {
                background : ${darken(0.1, selected)}
            }
        `;
    }}
`


const StyledButton = styled.button`
    display : inline-flex;
    outline : none;
    border : none;
    border-radius : 4px;
    color : white;
    font-weight : bold;
    cursor : pointer;
    padding-left : 1rem;
    padding-right : 1rem;

    height : 2.25rem;
    font-size : 1rem;

    
    ${colorStyles}
    

    & + &{
        margin-left : 1rem;
    }

`;


function Button({ children, color ,...rest}) {
    return (
        <StyledButton color ={color} {...rest}>{children}</StyledButton>
    );
    
}


Button.defaultProps = { //기본값
    color : 'blue'
}


export default Button;

위 접은글은 color까지 추가한 경우이다.

 

 

 

사이즈를 다양하게 하기

 

더보기

 

 

import React from 'react';
import styled, {css}from 'styled-components';
import {darken,lighten} from 'polished';

const colorStyles = css`
    ${({theme,color}) =>{
        const selected = theme.palette[color];
        return css`
            background : ${selected};
            &:hover {
                background : ${lighten(0.1, selected)}
            }
            &:active {
                background : ${darken(0.1, selected)}
            }
        `;
    }}
`

const sizeStyles = css`
    ${props => 
        props.size === 'large' && 
        css`
            height : 3rem;
            font-size : 1.25rem;
        `}
    ${props => 
        props.size === 'medium' && 
        css`
            height : 2.25rem;
            font-size : 1rem;
        `}
    ${props => 
        props.size === 'small' && 
        css`
            height : 1.75rem;
            font-size : 0.875rem;
        `}
`



const StyledButton = styled.button`
    display : inline-flex;
    outline : none;
    border : none;
    border-radius : 4px;
    color : white;
    font-weight : bold;
    cursor : pointer;
    padding-left : 1rem;
    padding-right : 1rem;


    
    ${colorStyles}
    ${sizeStyles}
    

    & + &{
        margin-left : 1rem;
    }

`;


function Button({ children, color , size, ...rest}) {
    return (
        <StyledButton color ={color} size = {size} {...rest}>{children}</StyledButton>
    );
    
}


Button.defaultProps = { //기본값
    color : 'blue',
    size : 'medium'
}


export default Button;

 

//App.js
import React from 'react'
import './App.css';
import styled, { ThemeProvider , css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동
import Button from './components/Button';

const AppBlock = styled.div`

width : 512px;
margin : 0 auto;
margin-top : 4rem;
border : 1px solid black;
padding : 1rem;
`;
  
const palette = {
  blue : '#228be6',
  gray : '#496057',
  pink : '#f06595'
}


const ButtonGroup = styled.div`
  & + & {
    margin-top : 1rem;
  }  
`;


function App() {
  return (
    <ThemeProvider theme={{
      palette
    }}>
      <AppBlock>
        <ButtonGroup>
          <Button size = 'large'>BUTTON</Button>
          <Button color = "gray">BUTTON</Button>
          <Button size = 'small'color = "pink">BUTTON</Button>
        </ButtonGroup>
      </AppBlock>
    </ThemeProvider>
  );
}


export default App;

 

 

const sizeStyles = css`
    ${props => 
        props.size === 'large' && 
        css`
            height : 3rem;
            font-size : 1.25rem;
        `}
    ${props => 
        props.size === 'medium' && 
        css`
            height : 2.25rem;
            font-size : 1rem;
        `}
    ${props => 
        props.size === 'small' && 
        css`
            height : 1.75rem;
            font-size : 0.875rem;
        `}
`

마찬가지로 size를 props로 넣어주고 위 코드를 추가하면 사이즈별 버튼을 만들 수 있다.

 

const sizes = {
    large : {
        height : '3rem',
        fontSize : '1.25rem'
    },
    medium : {
        height : '2.25rem',
        fontSize : '1rem'
    },
    small : {
        height : '1.75rem',
        fontSize : '0.875rem'
    }
};


const sizeStyles = css`
   /*크기*/

   ${({size}) => css`
    height : ${sizes[size].height};
    fontSize : ${sizes[size].fontSize};
   `}
`;

위방식으로 조금 더 간단하게 쓸 수 있다.

 

 

이러한 방식이 유지보수할때 조금더 편할수 있는 방법일 순 있으나 굳이 할 필요는 없다.

 

 

outline,fulWidth

 

이번엔 bool값으로 설정을 넣어줄 것이다. 방법은 위와 동일하다!

 

더보기

 

import React from 'react';
import styled, {css}from 'styled-components';
import {darken,lighten} from 'polished';

const colorStyles = css`
    ${({theme,color}) =>{
        const selected = theme.palette[color];
        return css`
            background : ${selected};
            &:hover {
                background : ${lighten(0.1, selected)}
            }
            &:active {
                background : ${darken(0.1, selected)}
            }
            ${props => props.outline &&
            css `
                color : ${selected};
                background : none;
                border : 1px solid ${selected};
                &:hover {
                    background : ${selected}
                    color : white;
                }
            `}
        `;
    }}
`;

const fullWidthStyle = css`
    ${props => props.fullWidth && css`
        width : 100%;
        justify-content : center;
        & + & {
            margin-left : 0;
            margin-top : 1rem;
        }
    `}
`;



const sizes = {
    large : {
        height : '3rem',
        fontSize : '1.25rem'
    },
    medium : {
        height : '2.25rem',
        fontSize : '1rem'
    },
    small : {
        height : '1.75rem',
        fontSize : '0.875rem'
    }
};


const sizeStyles = css`
   /*크기*/

   ${({size}) => css`
    height : ${sizes[size].height};
    fontSize : ${sizes[size].fontSize};
   `}
`;



const StyledButton = styled.button`
    display : inline-flex;
    outline : none;
    border : none;
    border-radius : 4px;
    color : white;
    font-weight : bold;
    cursor : pointer;
    padding-left : 1rem;
    padding-right : 1rem;


    & + &{
        margin-left : 1rem;
    }
    
    ${colorStyles}
    ${sizeStyles}
    ${fullWidthStyle}


`;


function Button({ children, color , size,outline,fullWidth, ...rest}) {
    return (
        <StyledButton color ={color} size = {size} outline = {outline} fullWidth = {fullWidth} {...rest}>{children}</StyledButton>
    );
    
}


Button.defaultProps = { //기본값
    color : 'blue',
    size : 'medium'
}


export default Button;

 

//App.js
import React from 'react'
import './App.css';
import styled, { ThemeProvider , css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동
import Button from './components/Button';

const AppBlock = styled.div`

width : 512px;
margin : 0 auto;
margin-top : 4rem;
border : 1px solid black;
padding : 1rem;
`;
  
const palette = {
  blue : '#228be6',
  gray : '#496057',
  pink : '#f06595'
}


const ButtonGroup = styled.div`
  & + & {
    margin-top : 1rem;
  }  
`;


function App() {
  return (
    <ThemeProvider theme={{
      palette
    }}>
      <AppBlock>
        <ButtonGroup>
          <Button size = 'large'>BUTTON</Button>
          <Button color = "gray">BUTTON</Button>
          <Button size = 'small'color = "pink">BUTTON</Button>
        </ButtonGroup>
        <ButtonGroup>
          <Button size = 'large' outline>BUTTON</Button>
          <Button color = "gray" outline>BUTTON</Button>
          <Button size = 'small'color = "pink" outline>BUTTON</Button>
        </ButtonGroup>
        <ButtonGroup>
          <Button size = 'large' fullWidth>BUTTON</Button>
          <Button color = "gray" fullWidth>BUTTON</Button>
          <Button size = 'small'color = "pink" fullWidth>BUTTON</Button>
        </ButtonGroup>
      </AppBlock>
    </ThemeProvider>
  );
}


export default App;

 

Styled-components Dialog만들기

 

 

이번엔 아래와 같은 dialog를 만들어 보겠다.

더보기

 

import React from 'react';
import styled from 'styled-components'
import Button from './Button';


const Darkbackground = styled.div`
    position : fixed;
    left : 0;
    top : 0;
    width : 100%;
    height : 100%;
    display: flex;
    align-items : center;
    justify-content : center;
    background : rgba(0,0,0,0.8);

    
`;

const DialogBlock = styled.div`
    width : 320px;
    padding : 1.5rem;
    background : white;
    border-radius : 2px;

    h3 {
        margin : 0;
        font-size : 1.5rem;
    }

    p {
        font-size : 1.125rem;
    }
`;

const ButtonGroup = styled.div`
    margin-top : 3rem;
    display : flex;
    justify-content : flex-end;
`;


const ShortMarginButton = styled(Button)`
    & + &{
        margin-left : 0.5rem;
    }
`; //상속받아서 사용


function Dialog({
    title,
    children,
    confirmText,
    cancelText,
    visible,
    onConfirm,
    onCancel
}) {

    if(!visible) return null; //visibile이 없으면 보여지지 않음

    return (
        <Darkbackground>
            <DialogBlock>
                <h3>{title}</h3>
                <p>{children}</p>
                <ButtonGroup>
                    <ShortMarginButton onClick = {onConfirm}color="gray">
                        {confirmText}
                    </ShortMarginButton>
                    <ShortMarginButton onClick = {onCancel}color="pink">
                        {cancelText}
                    </ShortMarginButton>
                </ButtonGroup>
            </DialogBlock>
        </Darkbackground>
    );
    
}


Dialog.defaultProps = {
    cancelText : '취소',
    confirmText : '확인'
}


export default Dialog;

 

//App.js
import React, {useState} from 'react'
import './App.css';
import styled, { ThemeProvider , css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동
import Button from './components/Button';
import Dialog from './components/Dialog';

const AppBlock = styled.div`

width : 512px;
margin : 0 auto;
margin-top : 4rem;
border : 1px solid black;
padding : 1rem;
`;
  
const palette = {
  blue : '#228be6',
  gray : '#496057',
  pink : '#f06595'
}


const ButtonGroup = styled.div`

  
  & + & {
    margin-top : 1rem;
  }  
`;


function App() {

  const [dialog,setDialog] = useState(false);  //보여질지 안보여질지 정하는 변수
  const onClick = () =>{
    setDialog(true);
  }

  const onConfirm= () =>{
    console.log('확인');
    setDialog(false);
  }
  const onCancel= () =>{
    console.log('취소');
    setDialog(false);
  }


  return (
    <ThemeProvider theme={{
      palette
    }}>
      <>
        <AppBlock>
          <ButtonGroup>
            <Button size = 'large'>BUTTON</Button>
            <Button color = "gray">BUTTON</Button>
            <Button size = 'small'color = "pink">BUTTON</Button>
          </ButtonGroup>
          <ButtonGroup>
            <Button size = 'large' outline>BUTTON</Button>
            <Button color = "gray" outline>BUTTON</Button>
            <Button size = 'small'color = "pink" outline>BUTTON</Button>
          </ButtonGroup>
          <ButtonGroup>
            <Button size = 'large' fullWidth>BUTTON</Button>
            <Button color = "gray" fullWidth>BUTTON</Button>
            <Button size = 'small'color = "pink" fullWidth>BUTTON</Button>
          </ButtonGroup>      

          <Button color = 'pink' size = 'large' onClick={onClick}>삭제</Button> 

        </AppBlock>
        <Dialog 
            title = "정말로 삭제하시겠습니까?"
            confirmText = "삭제"
            cancelText = "취소" 
            onConfirm={onConfirm}
            onCancel={onCancel}
            visible = {dialog}         
            > 데이터를 정말로 삭제하시겠습니까?</Dialog>
          
      </>
    </ThemeProvider>
  );
}


export default App;

간단하게 설명하면 회색 바탕화면들을 설정해두고, visible을 useState로 만든 이후에 그 값에 따라 보여지거나 안보여지거나를 설정하는 것이다.

 

 

그렇다면 나타나거나 사라질 때 좀더 부드럽게 부드럽게 전환하는 트렌지션을 구현해 보겠다.

 

keyframes

 

이전에 배운 keyframes를 사용하면 구현할 수 있다.

 

이를사용하기 위해서는

 

//Dialog.js
import styled, { keyframes } from 'styled-components'

불러줘야 사용할 수 있다.

 

 

 

//애니메이션 설정
const fadeIn = keyframes`   
    from{
        opacity : 0;
    }
    to {
        opacity : 1;
    }
`

const slideUp = keyframes`
    from{
        transform : translateY(200px);
    }
    to {
        transform : translateY(0px);
    }
`

다음과 같이 애니메이션 설정을 해두고

 

const Darkbackground = styled.div`
    position : fixed;
    left : 0;
    top : 0;
    width : 100%;
    height : 100%;
    display: flex;
    align-items : center;
    justify-content : center;
    background : rgba(0,0,0,0.8);

    animation-duration : 0.25s;
    animation-timing-function : ease-out;
    animation-name : ${fadeIn};   /* 나타날때 transition */
    animation-fill-mode : forwards; /* 유지 */    
`;  

const DialogBlock = styled.div`
    width : 320px;
    padding : 1.5rem;
    background : white;
    border-radius : 2px;

    h3 {
        margin : 0;
        font-size : 1.5rem;
    }

    p {
        font-size : 1.125rem;
    }


    animation-duration : 0.25s;
    animation-timing-function : ease-out;
    animation-name : ${slideUp};
    animation-fill-mode : forwards; /* 유지 */ 
`;

animation 설정을 해주면 킬때 설정이 완성된다!

 

취소할때 transition을 적용하는 방법은 조금 더 어렵다

 

import React , {useState, useEffect }from 'react';

우선 useState와 useEffect를 사용해야 한다.

 

 

삭제버튼이 눌렀을때는 변하는 시점을 직접 설정해주어야한다.

 

fadeIn과 SlideUp을 보면 0.25초동안 애니메이션이 실행되는 것을 확인할 수 있다.

 

다르게 생각하면 버튼을 누르고 0.25초가 지난 이후에 화면전환이 이루어져야 함을 알 수있다. 그래서 useState와 useEffect를 통해서 변하는 시점을 캐치한 후에, 0.25초가 지나고 화면전환이 이루어지게 설정해두면 된다.

더보기

 

//App.js
import React, {useState} from 'react'
import './App.css';
import styled, { ThemeProvider , css } from 'styled-components'   //css를 넣어야 ``가 styled-componets로 작동
import Button from './components/Button';
import Dialog from './components/Dialog';

const AppBlock = styled.div`

width : 512px;
margin : 0 auto;
margin-top : 4rem;
border : 1px solid black;
padding : 1rem;
`;
  
const palette = {
  blue : '#228be6',
  gray : '#496057',
  pink : '#f06595'
}


const ButtonGroup = styled.div`

  
  & + & {
    margin-top : 1rem;
  }  
`;


function App() {

  const [dialog,setDialog] = useState(false);  //보여질지 안보여질지 정하는 변수
  const onClick = () =>{
    setDialog(true);
  }

  const onConfirm= () =>{
    console.log('확인');
    setDialog(false);
  }
  const onCancel= () =>{
    console.log('취소');
    setDialog(false);
  }


  return (
    <ThemeProvider theme={{
      palette
    }}>
      <>
        <AppBlock>
          <ButtonGroup>
            <Button size = 'large'>BUTTON</Button>
            <Button color = "gray">BUTTON</Button>
            <Button size = 'small'color = "pink">BUTTON</Button>
          </ButtonGroup>
          <ButtonGroup>
            <Button size = 'large' outline>BUTTON</Button>
            <Button color = "gray" outline>BUTTON</Button>
            <Button size = 'small'color = "pink" outline>BUTTON</Button>
          </ButtonGroup>
          <ButtonGroup>
            <Button size = 'large' fullWidth>BUTTON</Button>
            <Button color = "gray" fullWidth>BUTTON</Button>
            <Button size = 'small'color = "pink" fullWidth>BUTTON</Button>
          </ButtonGroup>      

          <Button color = 'pink' size = 'large' onClick={onClick}>삭제</Button> 

        </AppBlock>
        <Dialog 
            title = "정말로 삭제하시겠습니까?"
            confirmText = "삭제"
            cancelText = "취소" 
            onConfirm={onConfirm}
            onCancel={onCancel}
            visible = {dialog}         
            > 데이터를 정말로 삭제하시겠습니까?</Dialog>
          
      </>
    </ThemeProvider>
  );
}


export default App;

 

//Dialog.js
import React , {useState, useEffect }from 'react';
import styled, { keyframes ,css} from 'styled-components'
import Button from './Button';

//애니메이션 설정
const fadeIn = keyframes`   
    from{
        opacity : 0;
    }
    to {
        opacity : 1;
    }
`

const slideUp = keyframes`
    from{
        transform : translateY(200px);
    }
    to {
        transform : translateY(0px);
    }
`


const fadeOut = keyframes`   
    from{
        opacity : 1;
    }
    to {
        opacity : 0;
    }
`

const slideDown = keyframes`
    from{
        transform : translateY(0px);
    }
    to {
        transform : translateY(200px);
    }
`


const Darkbackground = styled.div`
    position : fixed;
    left : 0;
    top : 0;
    width : 100%;
    height : 100%;
    display: flex;
    align-items : center;
    justify-content : center;
    background : rgba(0,0,0,0.8);

    animation-duration : 0.25s;
    animation-timing-function : ease-out;
    animation-name : ${fadeIn};   /* 나타날때 transition */
    animation-fill-mode : forwards; /* 유지 */    


    ${props => props.disappear && css `
        animation-name : ${fadeOut};
    `}
`;  

const DialogBlock = styled.div`
    width : 320px;
    padding : 1.5rem;
    background : white;
    border-radius : 2px;

    h3 {
        margin : 0;
        font-size : 1.5rem;
    }

    p {
        font-size : 1.125rem;
    }


    animation-duration : 0.25s;
    animation-timing-function : ease-out;
    animation-name : ${slideUp};
    animation-fill-mode : forwards; /* 유지 */ 


    ${props => props.disappear && css `
        animation-name : ${slideDown};
    `}
`;

const ButtonGroup = styled.div`
    margin-top : 3rem;
    display : flex;
    justify-content : flex-end;
`;


const ShortMarginButton = styled(Button)`
    & + &{
        margin-left : 0.5rem;
    }
`; //상속받아서 사용


function Dialog({
    title,
    children,
    confirmText,
    cancelText,
    visible,
    onConfirm,
    onCancel
}) {


    const [animate,setAnimate] = useState(false);  //보여주고 있다라는 정보
    const [localVisible,setLocalVisible] = useState(visible); //현재 상태가 true에서 false로 변함을 감지


    useEffect(() => {
        //visible true => false의 시점 캐치
        if ( localVisible && !visible) {
            setAnimate(true);
            setTimeout(() => setAnimate(false),250) //0.25초 이후에 삭제기능이 동작하게 됨
        }
        setLocalVisible(visible); //visible값이 바뀔때마다 해당값을 동기화시켜줌

    }, [localVisible,visible]); //deps

    if(!localVisible && !animate) return null; //visibile이 없으면 보여지지 않음

    return (
        <Darkbackground disappear = {!visible}>   {/*disappear 값 전달*/}
            <DialogBlock disappear = {!visible}>
                <h3>{title}</h3>
                <p>{children}</p>
                <ButtonGroup>
                    <ShortMarginButton onClick = {onConfirm}color="gray">
                        {confirmText}
                    </ShortMarginButton>
                    <ShortMarginButton onClick = {onCancel}color="pink">
                        {cancelText}
                    </ShortMarginButton>
                </ButtonGroup>
            </DialogBlock>
        </Darkbackground>
    );
    
}


Dialog.defaultProps = {
    cancelText : '취소',
    confirmText : '확인'
}


export default Dialog;

 

728x90

'FrontEnd > React' 카테고리의 다른 글

20_리액트_라우터  (0) 2021.12.30
19_리액트 API연동  (0) 2021.12.29
17_리액트_CSS Module  (0) 2021.12.24
16_리액트_컴포넌트스타일링  (0) 2021.12.24
15_리액트_유용한 tool  (0) 2021.12.24