import styled from "styled-components";
import Button from "./button.component";
interface modalProps {
success: boolean;
isModalOpen: boolean;
close?(): void;
successFunc?(): void;
label: string;
additional?: any;
width: string;
height: string;
background: string;
textColor: string;
dimmed: string;
fontSize?: string;
}
interface defaultPropsType {
width: string;
height: string;
background: string;
}
const Modal = ({
success,
isModalOpen,
additional,
label,
close,
successFunc,
width,
height,
background,
textColor,
dimmed,
fontSize,
}: modalProps) => {
return (
<div>
{isModalOpen && (
<ModalBackdrop dimmed={dimmed}>
<Wrapper width={width} height={height} background={background}>
<InsideBox>
<Body>
<Label textColor={textColor} fontSize={fontSize}>
{label.split("\n").map((letter, index) => (
<div key={index}>
{letter}
<br />
</div>
))}
</Label>
{additional ? (
<Contents textColor={textColor}>{additional}</Contents>
) : null}
</Body>
<ModalButton
className="close"
onClick={success ? successFunc : close}
>
확인
</ModalButton>
</InsideBox>
</Wrapper>
</ModalBackdrop>
)}
</div>
);
};
export default Modal;
isModalOpen이 true일 때에만 요소들이 보여진다.
ModalBackdrop은 모달이 열렸을 때 뒷 배경 딤처리를 해주기 위해 감싸진 박스이고, Wrapper가 모달 영역이다.
모달 내부는 Body부분과 ModalButton 부분으로 나누어진다.
먼저 모든 모달에 공통적으로 들어있는 내용물("잘못된 비밀번호입니다", "삭제하시겠습니까?" 등)은 Label 컴포넌트에 담긴다.
(가운데 줄바꿈 관련은 이 포스트에 정리해놓았다.)
그리고 만약 라벨 밑에 추가적인 내용이 들어간다면 additional prop이 true가 되어 추가 콘텐츠 Contents가 보여진다.
success 은 모달이 그냥 확인 버튼을 누르고 닫히기만 하면 되는 종류인지, 혹은 확인 버튼을 누르고 닫힐 때 별개의 함수가 실행돼야 하는지 구분해주는 prop이다.
import { useContext, useState } from "react";
import Modal from "../components/Modal";
import { WakeUpTimeContext } from "../contexts/wakeupTimeReducer.context";
export const useModal = () => {
const data = useContext(WakeUpTimeContext);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const openModal = (): void => {
setIsModalOpen(true);
};
const closeModal = (): void => {
setIsModalOpen(false);
};
const successFunc = (): void => {
closeModal();
localStorage.setItem('user-wakeup-time', JSON.stringify(data))
}
return {
Modal,
isModalOpen,
openModal,
closeModal,
successFunc,
};
};
success가 true이면 successFunc를 실행한다. 여기선 로컬 스토리지에 데이터를 저장하는 코드를 포함했다const Wrapper = styled.div<defaultPropsType>`
width: ${(props) => props.width};
height: ${(props) => props.height};
background-color: ${(props) => props.background};
border: none;
border-radius: 20px;
z-index: 1000;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
type dimmedProp = {
dimmed: string;
};
export const ModalBackdrop = styled.div<dimmedProp>`
//딤처리
z-index: 3;
position: fixed;
display: flex;
justify-content: center;
align-items: center;
background-color: ${(props) => props.dimmed};
top: 0;
left: 0;
right: 0;
bottom: 0;
`;
const InsideBox = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 32px;
width: fit-content;
height: fit-content;
`;
const Body = styled.div`
height: fit-content;
position: relative;
top: -1rem;
`;
type TextColor = {
textColor: string;
fontSize?: string;
};
const Label = styled.h6<TextColor>`
font-size: ${(props) => props.fontSize || "16px"};
font-style: normal;
font-weight: 500;
line-height: normal;
letter-spacing: -0.32px;
text-align: center;
margin-bottom: 0;
white-space: pre-wrap;
color: ${(props) => props.textColor};
`;
const Contents = styled.h6<TextColor>`
font-size: ${(props) => props.fontSize || "16px"};
font-style: normal;
font-weight: 500;
line-height: normal;
letter-spacing: -0.32px;
text-align: center;
position: relative;
top: 16px;
margin-bottom: 0;
white-space: pre-wrap;
color: ${(props) => props.textColor};
height: width;
`;
const ModalButton = styled(Button)`
display: flex;
width: 300px;
height: 52px;
padding: 12px 40px;
justify-content: center;
align-items: center;
flex-shrink: 0;
font-size: 16px;
position: relative;
top: 1.5rem;
`;
Modal.defaultProps = {
width: "330px",
height: "180px",
background: "#FFF",
textColor: "#4D4D4D",
dimmed: "rgba(0, 0, 0, 0.60);",
};