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

♻️ refactor: BottomSheet 컴포넌트 코드 최소화 리팩토링 #60

Merged
merged 1 commit into from
Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/components/common/BottomSheet/BasicModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import React from 'react';
import styled from 'styled-components';

const BasicModalWrapper = styled.div`
height: 192px;
margin: 0 16px;
padding: 16px;
max-height: 548px;
overflow-y: scroll;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
`;

export default function BasicModal({ children }) {
Expand Down
61 changes: 24 additions & 37 deletions src/components/common/BottomSheet/BottomSheet.jsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,38 @@
import React, { useState, useEffect, useRef } from 'react';
import { BottomSheetDim, ModalBox, HeaderModal } from './BottomSheetStyle';
import ListModal from './ListModal';
import BasicModal from './BasicModal';
import React, { useState, useEffect } from 'react';
import { BottomSheetDim, BottomSheetWrapper, ModalBox, ModalHandle } from './BottomSheetStyle';

// type: basic, list
function BottomSheet({ type = 'basic', isShow, setIsShow, children }) {
const [animate, setAnimate] = useState(false);
const [localVisible, setLocalVisible] = useState(isShow);
const wrapperRef = useRef(null);
const modalBoxRef = useRef(null);
export default function BottomSheet({ isShow, onClick, children }) {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
setLocalVisible(isShow);
}, [isShow]);
let modalTimer;

useEffect(() => {
if (localVisible && !isShow) {
setAnimate(true);
setTimeout(() => setAnimate(false), 250);
if (isShow) {
setIsVisible(true);
} else {
modalTimer = setTimeout(() => setIsVisible(false), 250);
} // 0.25초뒤에 없어짐
setLocalVisible(isShow);
}, [localVisible, isShow]);

// Dim 클릭했을 때, ModalBox부분을 제외한 영역이어야 동작하도록
const handleDimClick = (e) => {
if (wrapperRef.current && modalBoxRef.current && !modalBoxRef.current.contains(e.target)) {
setIsShow(false);
}
};

// ModalBox의 HeaderModal을 클릭했을 때 동작
const handleHeaderModalClick = () => {
setIsShow(false);
};
return () => {
if (modalTimer !== undefined) {
clearTimeout(modalTimer);
}
};
}, [isShow]);

if (!localVisible && !animate) return null;
if (!isVisible) {
return null;
}

return (
<>
<BottomSheetDim onClick={handleDimClick} disappear={!isShow} ref={wrapperRef}>
<ModalBox disappear={!isShow} ref={modalBoxRef}>
<HeaderModal onClick={handleHeaderModalClick} />
{type === 'list' ? <ListModal /> : <BasicModal>{children}</BasicModal>}
<BottomSheetWrapper>
<ModalBox isShow={isShow}>
{children}
<ModalHandle onClick={onClick} aria-label='모달 닫기' />
</ModalBox>
</BottomSheetDim>
<BottomSheetDim isShow={isShow} onClick={onClick} />
</BottomSheetWrapper>
</>
);
}

export default BottomSheet;
60 changes: 21 additions & 39 deletions src/components/common/BottomSheet/BottomSheetStyle.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import styled, { css, keyframes } from 'styled-components';
import styled, { keyframes } from 'styled-components';

const fadeIn = keyframes`
from {
Expand All @@ -23,13 +23,13 @@ const slideUp = keyframes`
}
to {
transform: translateY(calc(100% - ${({ LEN }) => (LEN < 425 ? LEN : 425)}px));
transform: translateY(0);
}
`;

const slideDown = keyframes`
from {
transform: translateY(calc(100% - ${({ LEN }) => (LEN < 425 ? LEN : 425)}px));
transform: translateY(0);
}
to {
Expand All @@ -38,66 +38,48 @@ const slideDown = keyframes`
`;

export const BottomSheetDim = styled.div`
position: absolute;
position: fixed;
top: 0;
width: 100%;
width: clamp(390px, 100%, 720px);
height: 100vh;
margin: 0 auto;
background-color: rgba(0, 0, 0, 0.3);
transition: background-color 0.25s ease-out;
display: flex;
flex-direction: column;
justify-content: space-between;
animation: ${fadeIn} 0.25s ease-out forwards;
${(props) =>
props.disappear &&
css`
animation-name: ${fadeOut};
`}
animation: ${(p) => (p.isShow ? fadeIn : fadeOut)} 0.3s ease-out;
transition: background-color 0.3s ease-out;
`;

export const ModalBox = styled.div`
width: inherit;
height: ${({ LEN }) => (LEN < 425 ? LEN : 425)}px;
border-radius: 1rem 1rem 0 0;
export const BottomSheetWrapper = styled.article`
width: 100%;
position: absolute;
bottom: 0;
`;

export const ModalBox = styled.div`
position: relative;
bottom: 0;
z-index: 50;
border-radius: 20px 20px 0 0;
padding-top: 48px;
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.colors.white};
animation: ${slideUp} 0.25s ease-out forwards;
${(props) =>
props.disappear &&
css`
animation-name: ${slideDown};
animation-timing-function: ease-in;
`}
animation: ${(p) => (p.isShow ? slideUp : slideDown)} 0.3s ease-out;
`;

export const HeaderModal = styled.button`
export const ModalHandle = styled.button`
width: 100%;
height: 48px;
position: relative;
position: absolute;
top: 0;
&::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 4px;
background-color: ${({ theme }) => theme.colors.gray100};
}
`;
37 changes: 19 additions & 18 deletions src/components/common/BottomSheet/ListModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,35 @@ import React from 'react';
import styled from 'styled-components';

const BottomSheetListWrapper = styled.ul`
max-height: 425px;
overflow-y: scroll;
::-webkit-scrollbar {
width: 0;
background: transparent;
}
margin-bottom: 10px;
`;

const ListItem = styled.li`
height: 48px;
display: flex;
align-items: center;
padding: 0 24px;
height: 46px;
padding: 14px 26px;
font-size: ${({ theme }) => theme.fontSize.sm};
cursor: pointer;
`;

const LEN = 0;

export default function ListModal({ items }) {
const LEN = items.length * 48;
const TYPES = {
profile: ['설정 및 개인정보', '로그아웃'],
myPost: ['삭제', '수정'],
userPost: ['신고하기'],
chat: ['채팅방 나가기'],
myComment: ['삭제'],
userComment: ['신고하기'],
};

export default function ListModal({ type, onClick }) {
return (
<BottomSheetListWrapper>
{items.map((item, index) => (
<ListItem key={index}>{item}</ListItem>
{TYPES[type].map((item, index) => (
<ListItem key={index} onClick={onClick}>
<button type='button' onClick={onClick}>
{item}
</button>
</ListItem>
))}
</BottomSheetListWrapper>
);
}

export { LEN };
1 change: 1 addition & 0 deletions src/layout/BasicLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const LayoutWrapper = styled.div`
margin: 0 auto;
position: relative;
background-color: #fff;
overflow-y: hidden;
`;

const LayoutMain = styled.div`
Expand Down