Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat#17 modal #18

Merged
merged 9 commits into from
Apr 17, 2024
4 changes: 4 additions & 0 deletions public/assets/exlclamation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
<div id="modal" />
{children}
</body>
</html>
);
}
27 changes: 27 additions & 0 deletions src/components/common/Modal/Modal.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@import '@/styles/_index.scss';

.layout {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
background: rgba(0, 0, 0, 0.8);
}

.box {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2rem;
width: 28rem;
padding: 3rem;
background: $white;
@include rounded-sm;
}
42 changes: 42 additions & 0 deletions src/components/common/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import useOutsideClick from '@/hooks/useOutsideClick';
import { allowScroll, preventScroll } from '@/utils/modal';
import styles from './Modal.module.scss';

type ModalProps = {
children: React.ReactNode;
onClose: () => void;
};

function ModalPortal({ children }: { children: React.ReactNode }) {
const [isRendering, setIsRendering] = useState(true);

useEffect(() => {
setIsRendering(false);
}, []);

if (isRendering) return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

괄호로 감싸주시면 좋을 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 {} 하기로했었쪄? 수정 하도록 하겠슴다.

return ReactDOM.createPortal(children, document.getElementById('modal') as HTMLElement) as JSX.Element;
}

export default function Modal({ children, onClose }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
useOutsideClick(modalRef, onClose);
useEffect(() => {
const prevScrollY = preventScroll();
return () => {
allowScroll(prevScrollY);
};
}, []);

return (
<ModalPortal>
<section className={styles.layout}>
<div className={styles.box} ref={modalRef}>
{children}
</div>
</section>
</ModalPortal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '@/styles/_index';

.text {
@include font-normal;
}

.button {
@include color-box;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Image from 'next/image';
import { EXCLAMATION } from '@/utils/constants';
import Modal from '../Modal';
import styles from './RegistrationModal.module.scss';

export default function RegistrationModal({ isModal, onClose }: { isModal: boolean; onClose: () => void }) {
return (
<div>
{isModal && (
<Modal onClose={onClose}>
<Image src={EXCLAMATION} alt="check" width={25} height={25} />
<h2 className={styles.text}>가게 정보를 먼저 등록해 주세요. </h2>
<button className={styles.button} type="button" onClick={onClose}>
확인
</button>
</Modal>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import '@/styles/_index';

.text {
@include font-normal;
}

.selectionBox {
display: flex;
justify-content: center;
width: 100%;
gap: 2rem;
}

.negativeButton {
@include color-box;
}

.positiveButton {
@include color-box(fill);
}
25 changes: 25 additions & 0 deletions src/components/common/Modal/RejectionModal/RejectionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Image from 'next/image';
import { CHECK } from '@/utils/constants';
import Modal from '../Modal';
import styles from './RejectionModal.module.scss';

export default function RejectionModal({ isModal, onClose }: { isModal: boolean; onClose: () => void }) {
return (
<div>
{isModal && (
<Modal onClose={onClose}>
<Image src={CHECK} alt="check" width={25} height={25} />
<h2 className={styles.text}>신청을 거절하시겠어요?</h2>
<div className={styles.selectionBox}>
<button className={styles.negativeButton} type="button" onClick={onClose}>
아니요
</button>
<button className={styles.positiveButton} type="button" onClick={onClose}>
</button>
</div>
</Modal>
)}
</div>
);
}
14 changes: 14 additions & 0 deletions src/hooks/useOutsideClick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { RefObject, useEffect } from 'react';

const useOutsideClick = (ref: RefObject<HTMLElement>, callback: () => void) => {
useEffect(() => {
const handleOutSectionClick = (event: MouseEvent) => {
const { target } = event;
if (ref.current && !ref.current.contains(target as Node)) callback();
};
document.addEventListener('click', handleOutSectionClick);
return () => document.removeEventListener('click', handleOutSectionClick);
}, [ref, callback]);
};

export default useOutsideClick;
6 changes: 6 additions & 0 deletions src/styles/_common.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
@import './_mixins';
@import './_color';

* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
2 changes: 1 addition & 1 deletion src/styles/_index.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@import './_mixins';
@import './_variables';
@import './_color';
18 changes: 18 additions & 0 deletions src/styles/_mixins.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '@/styles/_color';

@mixin text-base {
font-size: 1rem;
line-height: 1.6rem;
Expand Down Expand Up @@ -35,3 +37,19 @@
@mixin rounded-full {
border-radius: 50%;
}

@mixin color-box($fill: false, $color: $red-400) {
padding: 0.6rem 1rem;
border: 1px solid $color;
@include rounded-lg;

@if $fill {
background-color: $color;
color: $white;
}

@else {
background: none;
color: $color;
}
}
1 change: 1 addition & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const CHECK_WHITE = `${BASE_PATH}/check_white.svg`;
export const CHECK = `${BASE_PATH}/check.svg`;
export const CLOCK = `${BASE_PATH}/clock.svg`;
export const ENVELOPE = `${BASE_PATH}/envelope_square.svg`;
export const EXCLAMATION = `${BASE_PATH}/exclamation.svg`;
export const FACEBOOK = `${BASE_PATH}/facebook_square.svg`;
export const GPS = `${BASE_PATH}/gps.svg`;
export const INSTAGRAM = `${BASE_PATH}/instagram.svg`;
Expand Down
16 changes: 16 additions & 0 deletions src/utils/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const preventScroll = () => {
const currentScrollY = window.scrollY;
document.body.style.position = 'fixed';
document.body.style.width = '100%';
document.body.style.top = `-${currentScrollY}px`;
document.body.style.overflowY = 'scroll';
return currentScrollY;
};

export const allowScroll = (prevScrollY: number) => {
document.body.style.position = '';
document.body.style.width = '';
document.body.style.top = '';
document.body.style.overflowY = '';
window.scrollTo(0, prevScrollY);
};