Skip to content

Commit

Permalink
update: 모달제작 및 리코일 install
Browse files Browse the repository at this point in the history
  • Loading branch information
JoStar33 committed Apr 14, 2024
1 parent 5c2de70 commit 42130bb
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 3 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"react-hook-form": "^7.51.2",
"react-icons": "^5.0.1",
"react-router-dom": "^6.22.3",
"recoil": "^0.7.7",
"styled-components": "^6.1.8",
"styled-reset": "^4.5.2",
"use-mask-input": "^3.3.7",
Expand Down
17 changes: 15 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import GlobalStyle from '@/styles/GlobalStyles';
import Theme from './styles/Theme';
import Layout from './components/layouts';
import Theme from '@/styles/Theme';
import Layout from '@/components/layouts';
import { useRecoilValue } from 'recoil';
import { modalWithText } from '@/stores/modal';
import Portal from './components/common/Portal';
import Modal from '@/components/common/Modal';
import { AnimatePresence } from 'framer-motion';

export default function App() {
const modalWithTextValue = useRecoilValue(modalWithText);

return (
<Theme>
<GlobalStyle />
<Layout>
<></>
</Layout>
<Portal>
<AnimatePresence>
{modalWithTextValue.type === 'ALERT' && <Modal.Alert />}
{modalWithTextValue.type === 'CONFIRM' && <Modal.Confirm />}
</AnimatePresence>
</Portal>
</Theme>
);
}
38 changes: 38 additions & 0 deletions src/components/common/DarkBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import styled from 'styled-components';

interface IDarkBackground {
children?: React.ReactNode;
onClickClose?: () => void;
style?: React.CSSProperties;
}

export default function DarkBackground({ onClickClose, style, children }: IDarkBackground) {
React.useEffect(() => {
const targetElement = document.querySelector('body') as HTMLBodyElement;
targetElement.style.overflow = 'hidden';
return () => {
targetElement.style.overflow = 'visible';
};
}, []);

return (
<S.DarkBackground style={style} onClick={onClickClose}>
{children}
</S.DarkBackground>
);
}

const S = {
DarkBackground: styled.div`
z-index: 9;
position: fixed;
top: 0;
right: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.1);
`,
};
152 changes: 152 additions & 0 deletions src/components/common/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import styled from 'styled-components';
import { motion } from 'framer-motion';
import { useRecoilValue } from 'recoil';
import Portal from '@/components/common/Portal';
import DarkBackground from '@/components/common/DarkBackground';
import { textEllipsis } from '@/styles/Common';
import { modalWithText } from '@/stores/modal';
import { IoMdInformationCircleOutline } from 'react-icons/io';

const modalVariants = {
hidden: {
opacity: 0,
y: '-100%',
// x: '100%',
},
visible: {
opacity: 1,
y: '90%',
// x: '100%',
transition: {
stiffness: 150,
mass: 0.4,
damping: 10,
type: 'spring',
},
},
};

function Alert() {
const modalValue = useRecoilValue(modalWithText);
const computedDescTextArr = modalValue.descText?.split('\n');

return (
<Portal>
<DarkBackground>
<S.Modal variants={modalVariants} animate="visible" initial="hidden">
<div className="modal-wrapper">
<i className="info-icon">
<IoMdInformationCircleOutline size={20} />
</i>
<h3 className="title-text">{modalValue.titleText}</h3>
<div className="desc-text-container">{computedDescTextArr?.map((desc) => <p className="desc-text">{desc}</p>)}</div>
</div>
<button className="button-box__confirm" onClick={modalValue.onClickConfirm}>
{modalValue.confirmButtonText}
</button>
</S.Modal>
</DarkBackground>
</Portal>
);
}

function Confirm() {
const modalValue = useRecoilValue(modalWithText);
const computedDescTextArr = modalValue.descText?.split('\n');

return (
<Portal>
<DarkBackground>
<S.Modal variants={modalVariants} animate="visible" initial="hidden" exit="hidden">
<div className="modal-wrapper">
<i className="info-icon">
<IoMdInformationCircleOutline size={20} />
</i>
<h3 className="title-text">{modalValue.titleText}</h3>
<div className="desc-text-container">{computedDescTextArr?.map((desc) => <p className="desc-text">{desc}</p>)}</div>
</div>
<div className="button-box">
<button className="button-box__cancel" onClick={modalValue.onClickCancel}>
{modalValue.cancelButtonText}
</button>
<button className="button-box__confirm" onClick={modalValue.onClickConfirm}>
{modalValue.confirmButtonText}
</button>
</div>
</S.Modal>
</DarkBackground>
</Portal>
);
}

const S = {
Modal: styled(motion.article)`
background-color: #fff;
aspect-ratio: 16/9;
margin: 0 auto;
width: 400px;
box-shadow: rgb(0 0 0 / 10%) 0px 4px 16px 0px;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
will-change: transform;
padding: 15px;
z-index: 10;
.modal-wrapper {
width: 100%;
}
.info-icon {
text-align: center;
display: flex;
justify-content: center;
margin-top: 20px;
margin-bottom: 20px;
}
.title-text {
font-size: 18px;
font-weight: 600;
text-align: center;
margin-bottom: 20px;
}
.desc-text-container {
display: flex;
flex-direction: column;
margin: 10px 0 40px 0;
}
.desc-text {
text-align: center;
${textEllipsis}
font-size: 16px;
font-weight: 500;
padding: 0 10px;
}
.button-box {
display: flex;
width: 100%;
gap: 10px;
font-size: 2rem;
font-weight: 800;
&__cancel {
width: 50%;
height: 50px;
border-radius: 10px;
border: 1px solid black;
background-color: ${(props) => props.theme.colors.white};
}
&__confirm {
width: 50%;
height: 50px;
border-radius: 10px;
border: none;
background-color: ${(props) => props.theme.colors.main};
color: ${(props) => props.theme.colors.white};
}
}
`,
};

const Modal = { Alert, Confirm };

export default Modal;
6 changes: 6 additions & 0 deletions src/constants/recoilKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const recoilKeys = {
MODAL: 'MODAL',
MODAL_WITH_TEXT: 'MODAL_WITH_TEXT',
};

export default recoilKeys;
5 changes: 4 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from '@/App.tsx';
import { BrowserRouter } from 'react-router-dom';
import { RecoilRoot } from 'recoil';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<App />
<RecoilRoot>
<App />
</RecoilRoot>
</BrowserRouter>
</React.StrictMode>,
);
41 changes: 41 additions & 0 deletions src/stores/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import recoilKeys from '@/constants/recoilKeys';
import { atom, selector } from 'recoil';

interface IModalState {
type?: 'ALERT' | 'CONFIRM';
titleText?: string;
descText?: string;
confirmButtonText: string;
cancelButtonText: string;
onClickConfirm: (...args: any) => void;
onClickCancel: (...args: any) => void;
}

export const modalState = atom<IModalState>({
key: recoilKeys.MODAL,
default: {
type: undefined,
titleText: undefined,
descText: undefined,
confirmButtonText: '',
cancelButtonText: '',
onClickConfirm: () => {},
onClickCancel: () => {},
},
});

export const modalWithText = selector({
key: recoilKeys.MODAL_WITH_TEXT,
get: ({ get }) => {
const getModalState = get(modalState);
return {
type: getModalState.type,
titleText: getModalState.titleText,
descText: getModalState.descText,
confirmButtonText: getModalState.confirmButtonText,
cancelButtonText: getModalState.cancelButtonText,
onClickConfirm: getModalState.onClickConfirm,
onClickCancel: getModalState.onClickCancel,
};
},
});

0 comments on commit 42130bb

Please sign in to comment.