저번에 찍먹해본 모달창을 이번에는 직접 구현해보았다. 까먹을 수 있으니 기록해두기..
1. 모달을 띄울 backgroud wrap(StModalWrap)과 모달창(ModalContainer)을 만든다
모달창 backgroud wrap의 부모 요소(MainWrap)를 position relative로,
모달창 backgroud wrap(StModalWrap)을 fixed로 설정하고
모달창 컨테이너는 absolute로 설정한 후 가운데 정렬한다.
return (
<>
<ResetStyles />
<MainWrap>
<StModalWrap>
<ModalContainer></ModalContainer>
</StModalWrap>
<LayoutWrap>
{isRendered && <NavHeader />}
<Header />
{isRendered && <Outlet />}
</LayoutWrap>
</MainWrap>
</>
);
}
const StModalWrap = styled.div`
width: 100%;
min-height: 100vh;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
transition: all 0.3s;
`;
const ModalContainer = styled.div`
width: 350px;
height: 180px;
padding: 20px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 10px;
border: 1px solid black;
`;
const LayoutWrap = styled.div`
width: 710px;
min-height: 961px;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ddf2f9;
padding-bottom: 50px;
`;
const MainWrap = styled.div`
display: flex;
justify-content: center;
background-color: #eef9fd;
position: relative;
`;
2. 모달상태를 토글할 state를 만들고 모달창 안의 요소에 적용한다.
const [isModalOpen, setIsModalOpen] = useState(false);
<ModalContainer>
<StModalText>로그아웃하시겠습니까?</StModalText>
<div>
<ModifyButton onClick={handleLogoutConfirmButtonClick}>확인</ModifyButton>
<DeleteButton onClick={() => setIsModalOpen(false)}>취소</DeleteButton>
</div>
</ModalContainer>
3. 모달상태를 토글하는 state를 프롭스로 받아 StModalWrap 스타일을 조건부 렌더링하여 isModalOpen의 상태에 따라 모달이 열리고 닫히게 한다.
const StModalWrap = styled.div`
z-index: ${props => (props.$isModalOpen ? '10' : '-1')};
width: 100%;
min-height: 100vh;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
transition: all 0.3s;
opacity: ${props => (props.$isModalOpen ? '1' : '0')};
`;
모달의 열리고 닫힘을 z-index와 opacity로 조건부 렌더링하고 transition을 걸어주면 부드럽게 열리고 닫히는 모달창을 구현할 수 있다.
4. 프롭스 내려주기
나의 경우 로그아웃 버튼이 NavHeader에 있었고, 로그아웃 버튼을 클릭했을 때 모달창을 띄우기 위해선 뷰포트 전체를 사용해야 했기 때문에 NavHeader 의 부모 레이아웃에서 모달창을 만들었고 NavHeader에 모달상태 토글 프롭스를 넘겨주어 로그아웃 버튼을 클릭하면 set으로 모달 상태를 토글할 수 있게 하였다.
<>
<ResetStyles />
<MainWrap>
<StModalWrap $isModalOpen={isModalOpen}>
<ModalContainer>
<StModalText>로그아웃하시겠습니까?</StModalText>
<div>
<ModifyButton onClick={handleLogoutConfirmButtonClick}>확인</ModifyButton>
<DeleteButton onClick={() => setIsModalOpen(false)}>취소</DeleteButton>
</div>
</ModalContainer>
</StModalWrap>
<LayoutWrap>
{isRendered && <NavHeader setIsModalOpen={setIsModalOpen} />}
<Header />
{isRendered && <Outlet />}
</LayoutWrap>
</MainWrap>
</>
전체코드
// 모달과 상관없는 코드 생략
const [isModalOpen, setIsModalOpen] = useState(false);
const handleLogoutConfirmButtonClick = () => {
setIsModalOpen(false);
navigate('/login');
};
return (
<>
<ResetStyles />
<MainWrap>
<StModalWrap $isModalOpen={isModalOpen}>
<ModalContainer>
<StModalText>로그아웃하시겠습니까?</StModalText>
<div>
<ModifyButton onClick={handleLogoutConfirmButtonClick}>확인</ModifyButton>
<DeleteButton onClick={() => setIsModalOpen(false)}>취소</DeleteButton>
</div>
</ModalContainer>
</StModalWrap>
<LayoutWrap>
{isRendered && <NavHeader setIsModalOpen={setIsModalOpen} />}
<Header />
{isRendered && <Outlet />}
</LayoutWrap>
</MainWrap>
</>
);
}
const StModalWrap = styled.div`
z-index: ${props => (props.$isModalOpen ? '10' : '-1')};
width: 100%;
min-height: 100vh;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
transition: all 0.3s;
opacity: ${props => (props.$isModalOpen ? '1' : '0')};
`;
const StModalText = styled.span`
font-family: sans-serif;
font-size: 18px;
margin: auto;
text-align: center;
`;
const ModalContainer = styled.div`
width: 350px;
height: 180px;
padding: 20px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 10px;
border: 1px solid black;
`;
const LayoutWrap = styled.div`
width: 710px;
min-height: 961px;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ddf2f9;
padding-bottom: 50px;
`;
const MainWrap = styled.div`
display: flex;
justify-content: center;
background-color: #eef9fd;
position: relative;
`;
export default AuthLayout;
'Today I Learned' 카테고리의 다른 글
인증과 인가 (0) | 2024.02.23 |
---|---|
HTTP GET sort로 데이터 정렬해서 가져오기 (0) | 2024.02.22 |
2024.02.21 TIL - PATCH, 로컬스토리지 (0) | 2024.02.21 |
개발자 도구로 디버깅하기 (0) | 2024.02.21 |
파일 탐색기 버튼 input type='file' 커스텀하여 사용하기 (0) | 2024.02.21 |