[ 프로젝트 ]/빨간 망토

모달/탭 UI 구현하기

디디 ( DD ) 2023. 6. 14. 23:22

 

 

(컴포넌트 구조)

 

   마이페이지

      ↓       ↓     

   모달     탭   

 

 

 

 

◆ 모달 (Modal)

 

마이페이지 모달 시연 화면

 

 

// 먼저, 부모 컴포넌트인 <마이페이지>에 modal상태를 추가한다.
const [modal, setModal] = useState(false);

// 모달 창을 끌 때 필요한 (모달 컴포넌트에 내려 줄) 함수를 만든다. 
const onCancel = () => {
  setModal(false);
};

// 수정하기 버튼을 누르면 modal상태를 true로 바꿔준다.
// 모달 컴포넌트는 modal상태가 true인 경우에만 나타날 수 있게 한다. 
return(
  <>
    {modal && (
      <MyPageModal
        onCancel={onCancel}
      />
    )}
    <button type="button" onClick={() => setModal(true)}>
      수정하기
    </button>
  </>
)

 

 

// 자식 컴포넌트인 <모달>에서 앞서 내려준 함수를 받은 후,
// X 버튼을 눌렀을 때 실행되도록(모달 창이 꺼지도록) 연결한다. 
export default function Modal({ onCancel }) {
  return(
    <div> <!-- 모달창이 되어줄 div -->
      <button type="button" onClick={onCancel}>
        X
      </button>
    </div>
  )
}

 

 

(+) 모달 창의 뒤로 블러 처리를 하고 싶다면, 모달 창이 되어줄 div를 감싸는 백그라운드 div를 하나 더 만든 후, 아래와 같이 스타일을 추가해주면 된다. 

 

// 백그라운드 div

width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px; // 여기까지는 배경을 전체 화면으로 맞추기 위함
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
z-index: 1; // 모달 뒤 레이어에 위치시키기


// 모달 창 div

position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); // 여기까지는 모달 창 위치를 가운데로 맞추기 위함
z-index: 999; // 배경 앞 레이어에 위치시키기

 

 

 

 

◆ 탭 (Tab)

 

마이페이지 탭 시연 화면

 

 

import { useState } from "react";
import styled from "styled-components";

const TabContainer = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: calc(4% + 3rem) 0 2rem;
  > ul {
    display: flex;
    > li {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 3rem;
      width: 20rem;
      color: var(--font-color-light);
      font-weight: 600;
      background-color: #e7d1d1;
      border-radius: 40px 40px 0 0;
      cursor: pointer;
      &.active {
        background-color: #fff;
        color: var(--primary-color);
      }
      @media (max-width: 500px) {
        height: 2.5rem;
        width: 12rem;
      }
    }
  }
`;

const TabContent = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  height: 25rem;
  width: 40rem;
  background-color: #fff;
  @media (max-width: 500px) {
    height: 16rem;
    width: 24rem;
  }
`;

export default function Tab() {
  const [activeTab, setActiveTab] = useState(0);
  const tabs = [
    { id: 0, name: "의뢰한 심부름", content: <div>(1) 준비 중입니다.</div> },
    { id: 1, name: "수행한 심부름", content: <div>(2) 준비 중입니다.</div> },
  ];

  return (
    <TabContainer>
      <ul>
        {tabs.map(tab => (
          <li key={tab.id} onClick={() => setActiveTab(tab.id)} className={activeTab === tab.id ? "active" : ""}>
            {tab.name}
          </li>
        ))}
      </ul>
      {tabs
        .filter(tab => activeTab === tab.id)
        .map(tab => (
          <TabContent key={tab.id}>{tab.content}</TabContent>
        ))}
    </TabContainer>
  );
}

 

탭 컴포넌트는 길지 않아서 코드를 그대로 가져와 봤다. 

로직을 설명하자면, 먼저 activeTab이라는 상태를 하나 만들어 준다. 그다음 탭 관련 정보가 담긴 tabs라는 배열을 만든다. 탭은 크게 두 부분으로 나뉘는데, 하나는 각 탭의 이름이 적힌 리스트 부분이고, 다른 하나는 각 탭의 내용이 들어가는 콘텐츠 부분이다. 리스트 부분의 이름을 클릭하면, 해당 이름을 가진 탭의 id로 activeTab 상태가 바뀌게 된다. activeTab이 되면, 즉 탭이 활성화되면, active라는 클래스가 적용되고, 흰 배경과 붉은 글씨로 리스트의 스타일이 바뀐다. 그리고 이에 맞춰 콘텐츠 부분의 내용도 활성화 상태인 탭의 내용으로 바뀐다.