ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React, 컴포넌트 디자인, styled-component 적용
    Topic/React | Redux 2022. 1. 4. 22:17
    반응형

    리액트 컴포넌트 디자인 실습  ✔️

     

    리액트 모달 창 띄우기  ☑️

    삼항 연산자를 이용한 조건부 렌더링을 활용한다.
    alert창과 다르게 어두운 배경 태그 내부에 창 태그를 삽입해 구현한다.

     

    import { useState } from 'react';
    import styled from 'styled-components';
    
    export const ModalContainer = styled.div`
    // Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다.
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100%;
      position: relative;
    `;
    
    export const ModalBackdrop = styled.div`ㅁ
     // Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다.
      position: fixed;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      background-color: rgba(0,0,0,0.3);
      z-index: 1;
    `;
    
    export const ModalBtn = styled.button`
      background-color: #4000c7;
      text-decoration: none;
      border: none;
      padding: 20px;
      color: white;
      border-radius: 30px;
      cursor: grab;
    `;
    
    export const ModalView = styled.div.attrs(props => ({
      // attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다.
      role: 'dialog'
    }))`
    // TODO : Modal창 CSS를 구현합니다.
      position: absolute;
      top: calc(50vh - 100px);
      left: calc(50vw - 200px);
      background-color: white;
      display: flex;
      justify-content: center;
      align-items: center;
      border-radius: 10px;
      width: 400px;
      height: 200px;
      >.close-btn{
        position: absolute;
        top: 2px;
        right: 7px;
        cursor: pointer;
      }
    `;
    
    export const Modal = () => {
      const [isOpen, setIsOpen] = useState(false);
    
      const openModalHandler = () => {
        // isOpen의 상태를 변경하는 메소드를 구현합니다.
        setIsOpen(!isOpen);
      };
    
      return (
        <>
          <ModalContainer onClick={openModalHandler}>
            <ModalBtn 
            // 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다. 
            >
              {isOpen ? 'Opened!' : 'Open Modal'}
              {/*조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */}
            </ModalBtn>
            {/*조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */}
            {isOpen ? <ModalBackdrop onClick={openModalHandler}>
                        <ModalView>
                          <div className='close-btn'>닫기
                          </div>
                          <div>HELLO CODESTATES!</div>
                        </ModalView>
                      </ModalBackdrop> : null}
          </ModalContainer>
        </>
      );
    };

     

    리액트 토글 구현하기  ☑️

    토글 버튼을 직접 만들어보는 과정에서 Styled-component를 사용했고, 
    버튼 클릭에 따른 스타일을 적용하는 방법을 익혔다.

    import { useState } from 'react';
    import styled from 'styled-components';
    
    const ToggleContainer = styled.div`
      position: relative;
      margin-top: 8rem;
      left: 47%;
      cursor: pointer;
    
      > .toggle-container {
        width: 50px;
        height: 24px;
        border-radius: 30px;
        background-color: #8b8b8b;
        // .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
        &.toggle--checked {
          background-color: green;
        }
      }
    
      > .toggle-circle {
        position: absolute;
        top: 1px;
        left: 1px;
        width: 22px;
        height: 22px;
        border-radius: 50%;
        background-color: #ffffff;
        transition: left 0.02s ease-in 0.1s;
        // .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
        &.toggle--checked {
          left: 27px;
          background-color: #ffffff;
          transition: left 0.02s ease-in 0.1s;
        }
      }
    `;
    
    const Desc = styled.div`
      // 설명 부분의 CSS를 구현합니다.
      text-align: center;
    `;
    
    export const Toggle = () => {
      const [isOn, setisOn] = useState(false);
    
      const toggleHandler = () => {
        // isOn의 상태를 변경하는 메소드를 구현합니다.
        setisOn(!isOn)
      };
    
      return (
        <>
          <ToggleContainer onClick={toggleHandler}>
            {/* 아래에 div 엘리먼트 2개가 있습니다. 각각의 클래스를 'toggle-container', 'toggle-circle' 로 지정하세요. */}
            {/* TIP : Toggle Switch가 ON인 상태일 경우에만 toggle--checked 클래스를 div 엘리먼트 2개에 모두 추가합니다. 조건부 스타일링을 활용하세요. */}
            <div className={'toggle-container' + (isOn ? ' toggle--checked' : '')}/>
            <div className={'toggle-circle' + (isOn ? ' toggle--checked' : '')}/>
          </ToggleContainer>
          
          
          <Desc>
            {isOn ? 'Toggle Switch ON' : 'Toggle Switch OFF'}
          </Desc>
        </>
      );
    };

     

    리액트 탭 구현하기  ☑️

    텍스트가 있는 탭을 클릭하면 사용자에게 변화를 알려주는 탭을 직접 구현해봤다.

    import { useState } from 'react';
    import styled from 'styled-components';
    
    // TODO: Styled-Component 라이브러리를 활용해 TabMenu 와 Desc 컴포넌트의 CSS를 구현합니다.
    
    const TabMenu = styled.ul`
      background-color: #dcdcdc;
      color: rgba(73, 73, 73, 0.5);
      font-weight: bold;
      display: flex;
      flex-direction: row;
      justify-items: center;
      align-items: center;
      list-style: none;
      margin-bottom: 7rem;
    
      .submenu {
        ${'' /* 기본 Tabmenu 에 대한 CSS를 구현합니다. */}
        background-color: white;
        color: black;
        padding: 10px;
        padding-left: 40px;
        padding-right: 40px;
      }
    
      .focused {
        ${'' /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다.  */}
        background-color: royalblue;
      }
    
      & div.desc {
        text-align: center;
      }
    `;
    
    const Desc = styled.div`
      text-align: center;
    `;
    
    export const Tab = () => {
      // TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한
      // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다.
      const [currentTab, setCurrentTab] = useState(0)
    
      const menuArr = [
        { name: 'Tab1', content: 'Tab menu ONE' },
        { name: 'Tab2', content: 'Tab menu TWO' },
        { name: 'Tab3', content: 'Tab menu THREE' },
      ];
    
      const selectMenuHandler = (index) => {
        // TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다
        // 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요.
        setCurrentTab(index);
      };
    
      return (
        <>
          <div>
            <TabMenu>
              {/* map을 이용한 반복으로 코드를 작성합니다.*/}
              {/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며, 
                      나머지 2개의 tab은 'submenu' 가 됩니다.*/}
              {
                menuArr.map((item, i)=> {
                  return <li className={i === currentTab ? 'submenu focused' : 'submenu'}
                    onClick={() => selectMenuHandler(i)}>
                      {item.name}
                  </li>
                })
              }
            </TabMenu>
            <Desc>
              {/* 현재 선택된 메뉴 따른 content를 표시하세요*/}
              <p>{menuArr[currentTab].content}</p>
            </Desc>
          </div>
        </>
      );
    };

     

    리액트 태그 구현하기  ☑️

    중복되는 태그는 삽입하지 못하고, x버튼을 클릭해 삽입된 태그들을 삭제하는 기능을 구현해보았다.

    import { useState } from 'react';
    import styled from 'styled-components';
    
    // TODO: Styled-Component 라이브러리를 활용해 여러분만의 tag 를 자유롭게 꾸며 보세요!
    
    export const TagsInput = styled.div`
      margin: 8rem auto;
      display: flex;
      align-items: flex-start;
      flex-wrap: wrap;
      min-height: 48px;
      width: 480px;
      padding: 0 8px;
      border: 1px solid rgb(214, 216, 218);
      border-radius: 6px;
    
      > ul {
        display: flex;
        flex-wrap: wrap;
        padding: 0;
        margin: 8px 0 0 0;
    
        > .tag {
          width: auto;
          height: 32px;
          display: flex;
          align-items: center;
          justify-content: center;
          color: #fff;
          padding: 0 8px;
          font-size: 14px;
          list-style: none;
          border-radius: 6px;
          margin: 0 8px 8px 0;
          background: #4000c7;
            > .tag-close-icon {
            display: block;
            width: 16px;
            height: 16px;
            line-height: 16px;
            text-align: center;
            font-size: 14px;
            margin-left: 8px;
            color: #4000c7;
            border-radius: 50%;
            background: #fff;
            cursor: pointer;
          }
        }
      }
    
      > input {    
        flex: 1;
        border: none;
        height: 46px;
        font-size: 14px;
        padding: 4px 0 0 0;
        :focus {
        outline: transparent;
      }
      }
    
      &:focus-within {
        border: 1px solid #4000c7;
      }
    
    `;
    
    export const Tag = () => {
      const initialTags = ['CodeStates', 'kimcoding'];
    
      const [tags, setTags] = useState(initialTags);
      const removeTags = (indexToRemove) => {
        // TODO : 태그를 삭제하는 메소드를 완성하세요.
        setTags(tags.filter(item => {
          return item !== tags[indexToRemove]
        }))
      };
      
      const addTags = (event) => {
        // TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요. 
        // 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다.
        // - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
        // - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
        // - 태그가 추가되면 input 창 비우기
        if (event.key === 'Enter') {
          if (event.target.value && !(tags.includes(event.target.value))) {
            setTags(tags.concat(event.target.value))
            event.target.value = ''
          }
        }
        }
      
    
      return (
        <>
          <TagsInput>
            <ul id='tags'>
              {tags.map((tag, index) => (
                <li key={index} className='tag'>
                  <span className='tag-title'>{tag}</span>
                  <span className='tag-close-icon' onClick={() => removeTags(index)}>x
                    {/* TODO :  tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고,
                                삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */}
                  </span>
                </li>
              ))}
            </ul>
            <input
              className='tag-input'
              type='text'
              onKeyUp={(e)=> addTags(e)}
              placeholder='Press enter to add tags'
            />
          </TagsInput>
        </>
      );
    };

    반응형
Designed by LEO.