Skip to content

Commit

Permalink
[FE] 공통 컴포넌트인 모달을 만든다. (#110)
Browse files Browse the repository at this point in the history
* fix: stylelint를 통한 css 속성 정렬 기능 오류 수정

- stylelint 버전16과 충돌되는 플러그인 삭제 : stylelint-config-prettier, stylelint-prettier
- css 정렬에 필요하지 않은 플러그인 삭제 : stylelint-config-standard, stylelint-config-styled-componented, stylelint-webpack-plugin
- 추가로 설치한 플러그인: postcss-syntax, @stylelint/postcss-css-in-js
- stylelint 적용 script 추가
- .stylelintrc.json 수정 : css 관련 rule 설정

* refactor: stylelint 적용에 따른 css 속성 정렬

* fix : 절대 경로 사용 시 오류 수정

오류 : eslintimport/no-unresolved

* chore: eslintrc.cjs 에서 불필요한 코드 삭제

 node 환경 setting 삭제

* style: eslint 적용에 따른 리뷰 상세페이지 import 순서 정리

* refactor: formatDate를 utils/date 파일로 이동

* design: theme에 colors,, fontSize 변경 및 borderRadius 추가

* feat: MultilineTextViewer 컴포넌트 생성

- 개행이 포함된 string에 개행을 적용해서 보여주는 컴포넌트

* feat: 깃허브 저장소 이미지 컴포넌트 생성

* feat: 리뷰와 관련된 날짜 UI 컴포넌트 생성

* featr: LockButton 삭제 LockToggle 추가

* refactor: 피그마 디자인 변경에 따른 ReviewDescription 변경

* feat: ReviewComment 컴포넌트 생성

* refactor: ReviewViewSection -> ReviewSection 으로 변경 및 리팩토링

- 불필요한  컴포넌트 삭제 : RevieAnswer , ReviewQuestion

* refactor: DetailedReviewPage 리팩토링

- 목데이터 변경
-  추가 및 변경된 컴포넌트를 사용해 리뷰 상세페이지 컴포넌트(DetailedReviewPage) 리팩토링
- DetailedReviewPage 폴더의 styles.ts 삭제

* refactor: review에 대한 타입 변경

* design : ReviewDate의 클론 스타일 적용

* feat: KeywordSection 컴포넌트 생성

- 리뷰 상세 페이지 키워드 부분 컴포넌트 생성

* feat: ReviewSectionHeader 컴포넌트 생성 및 적용

- 리뷰 상세보기에서 반복되는 질문,키워드 헤더부분을 컴포넌트로 분리

* design : 리뷰 상세페이지에 width 변경

* refactor: DetailedReview의 목데이터 변경 및 리팩토링

- 타입 변경에 따른 목 데이터 변경
- KeywordSection 적용

* design : formWidth를 theme에 추가 및 리뷰 작성/리뷰 상세 페이지에 적용

* fix: Layout에서 가로 스크롤 생기는 오류 수정

- 100vw는 스크롤을 포함한 뷰포트 너비라서 100%으로 수정

* feat: 리뷰 상페이지 router에 라우터 파라미터 적용 및 관련 설정 변경

- 데모데이를 위해 현재 데이터베이스에 있는 리뷰 상세페이지 id를 sidebar의 리뷰 상세페이지 메뉴 link에 적용
- 리뷰 상세페이지(DetailedReviewPage)의 api 핸들러 수정

* docs: 변수명 변경 (isLock -> isPublic)

* refactor: 깃헙 저장소 로고 주소 변수명 변경

- projectImgSrc -> thumbnailUrl

* ci: msw 관련 패키지 설치

* ci: msw 관련 설정파일 추가

- 브라우저 환경, node 환경에서 msw로 목서버 사용할 수 있도록 관련 파일 추가

* feat: mock 핸들러 추가 및 상세 리뷰 페이지 목 데이터 추가

* feat:  root에서 목서버 사용할 수 있도록함

* refactor: endpoint 수정

- env 에서 서버 주소 끝에 슬래시 넣는 것으로 통일

* feat: 상세 리뷰 페이지(detailedReviewPage)에 목서버 연결 및 관련 코드 수정

- 상태명 변경: detailReview -> detailedReview
- detailedReview 타입에 null 추가 및 그에 따른 오류 핸들링 추가
- deadline에 string 타입으로 response로 전달되어서 new Date로 감싸서 props로 전달

* docs: indexhtml의 title 변경

* style: apis/review.ts 의 import 관련 eslint rule 적용에 따른 수정

* fix: ts에서 process 읽지 못하는 오류 수정

* fix: webpack dev server script 복원

* feat: ModalPortal 셍성

* feat: SideModal 컴포넌트, useSide  셍성 및 Sidebar에 적용

* feat: ModalBackground 컴포넌트 생성 및 적용

* fix: 모달 열릴 때 스크롤바 막는 기능 오류 수정

* design : ModalPortal 사이즈 단위 변경 (% -> vw, vh)

* feat: Button 컴포넌트가 button 속성을 props로 받을 수 있도록 수정

* feat: ConfirmModal 생성

* refactor: index.tsx에서 모달을 꺼낼 수 있도록 리팩토링

* refactor: PropsWithChildren 수정

- PropsWithChildren를 import 하지 않고 React에서 바로 쓸 수 있도록 React.PropsWithChildren로  수정
  • Loading branch information
BadaHertz52 authored Jul 25, 2024
1 parent a178511 commit b43b0b5
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 32 deletions.
25 changes: 8 additions & 17 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import { useState } from 'react';
import { Outlet } from 'react-router';

import { Main, PageLayout, Sidebar, Topbar } from './components';
import { Main, PageLayout, Sidebar, Topbar, SideModal } from './components';
import { useSidebar } from './hooks';

const App = () => {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isSidebarHidden, setIsSidebarHidden] = useState(true);

const closeSidebar = () => {
setIsSidebarOpen(false);
setTimeout(() => {
setIsSidebarHidden(true);
}, 1000);
};

const openSidebar = () => {
setIsSidebarHidden(false);
setIsSidebarOpen(true);
};
const { isSidebarHidden, isSidebarModalOpen, closeSidebar, openSidebar } = useSidebar();

return (
<PageLayout>
{!isSidebarHidden && <Sidebar closeSidebar={closeSidebar} isSidebarOpen={isSidebarOpen} />}
{isSidebarModalOpen && (
<SideModal isSidebarHidden={isSidebarHidden}>
<Sidebar closeSidebar={closeSidebar} />
</SideModal>
)}
<Topbar openSidebar={openSidebar} />
<Main>
<Outlet />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ButtonType } from '@/types/styles';

import * as S from './styles';

interface ButtonProps {
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
buttonType: ButtonType;
text: string;
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/common/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { default as ProjectImg } from './ProjectImg';
export { default as ReviewDate } from './ReviewDate';
export { default as ReviewComment } from './ReviewComment';
export { default as MultilineTextViewer } from './MultilineTextViewer';
export * from './modals';
47 changes: 47 additions & 0 deletions frontend/src/components/common/modals/ConfirmModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

import { ButtonType } from '@/types/styles';

import Button from '../../Button';
import ModalBackground from '../ModalBackground';
import ModalPortal from '../ModalPortal';

import * as S from './styles';

interface ConfirmModalButton {
type: ButtonType;
text: string;
handleClick: (e: React.MouseEvent) => void;
}

interface ConfirmModalProps {
confirmButton: ConfirmModalButton;
cancelButton: ConfirmModalButton;
children: React.ReactNode;
}

const ConfirmModal: React.FC<React.PropsWithChildren<ConfirmModalProps>> = ({
children,
confirmButton,
cancelButton,
}) => {
const buttonList = [confirmButton, cancelButton];
return (
<ModalPortal>
<ModalBackground>
<S.ConfirmModalInnerWrapper>
<S.ConfirmModalInner>
<S.Contents>{children}</S.Contents>
<S.ButtonContainer>
{buttonList.map(({ type, text, handleClick }) => (
<Button key={text} buttonType={type} text={text} onClick={handleClick} />
))}
</S.ButtonContainer>
</S.ConfirmModalInner>
</S.ConfirmModalInnerWrapper>
</ModalBackground>
</ModalPortal>
);
};

export default ConfirmModal;
42 changes: 42 additions & 0 deletions frontend/src/components/common/modals/ConfirmModal/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import styled from '@emotion/styled';

export const ConfirmModalInnerWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
`;

export const ConfirmModalInner = styled.div`
display: flex;
flex-direction: column;
gap: 3.2rem;
justify-content: space-between;
padding: 3.2rem;
background-color: ${({ theme }) => theme.colors.white};
border-radius: ${({ theme }) => theme.borderRadius.basic};
`;

export const Contents = styled.div`
display: flex;
align-items: center;
min-width: 25rem;
min-height: 10rem;
max-height: 40vh;
`;

export const ButtonContainer = styled.div`
display: flex;
gap: 10%;
align-items: center;
justify-content: space-between;
button {
width: 40%;
max-width: 20rem;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import * as S from './styles';

const ModalBackground = ({ children }: React.PropsWithChildren) => {
return <S.ModalBackground>{children}</S.ModalBackground>;
};

export default ModalBackground;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from '@emotion/styled';

export const ModalBackground = styled.div`
width: 100%;
height: 100%;
background-color: ${({ theme }) => theme.colors.sidebarBackground};
`;
30 changes: 30 additions & 0 deletions frontend/src/components/common/modals/ModalPortal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PropsWithChildren, useEffect } from 'react';
import { createPortal } from 'react-dom';

import * as S from './styles';

interface ModalPortalProps {
id?: string;
}

const ModalPortal: React.FC<PropsWithChildren<ModalPortalProps>> = ({ children: Modal, id }) => {
const preventBodyScroll = () => {
document.body.style.overflow = 'hidden';
};

const allowBodyScroll = () => {
document.body.style.overflow = '';
};

useEffect(() => {
preventBodyScroll();

return () => {
allowBodyScroll();
};
});

return createPortal(<S.ModalPortal id={id || 'modal-portal'}>{Modal}</S.ModalPortal>, document.body);
};

export default ModalPortal;
13 changes: 13 additions & 0 deletions frontend/src/components/common/modals/ModalPortal/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from '@emotion/styled';

export const ModalPortal = styled.div`
position: fixed;
z-index: ${({ theme }) => theme.zIndex.modal};
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
`;
23 changes: 23 additions & 0 deletions frontend/src/components/common/modals/SideModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { PropsWithChildren } from 'react';

import ModalPortal from '@/components/common/modals/ModalPortal';

import ModalBackground from '../ModalBackground';

import * as S from './styles';

interface SideModalProps {
isSidebarHidden: boolean;
}

const SideModal: React.FC<PropsWithChildren<SideModalProps>> = ({ children: Sidebar, isSidebarHidden }) => {
return (
<ModalPortal id="sidebarModal-portal">
<ModalBackground>
<S.SidebarWrapper $isSidebarHidden={isSidebarHidden}>{Sidebar}</S.SidebarWrapper>
</ModalBackground>
</ModalPortal>
);
};

export default SideModal;
12 changes: 12 additions & 0 deletions frontend/src/components/common/modals/SideModal/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from '@emotion/styled';

interface SidebarWrapperProps {
$isSidebarHidden: boolean;
}

export const SidebarWrapper = styled.div<SidebarWrapperProps>`
position: absolute;
top: 0;
left: ${(props) => (props.$isSidebarHidden ? '-100%' : 0)};
transition: left 1s ease-in-out;
`;
2 changes: 2 additions & 0 deletions frontend/src/components/common/modals/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as SideModal } from './SideModal';
export { default as ConfirmModal } from './ConfirmModal';
5 changes: 2 additions & 3 deletions frontend/src/components/layouts/Sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ const PATH = {
};

interface SidebarProps {
isSidebarOpen: boolean;
closeSidebar: () => void;
}

const Sidebar = ({ isSidebarOpen, closeSidebar }: SidebarProps) => {
const Sidebar = ({ closeSidebar }: SidebarProps) => {
const location = useLocation();

const menuItems = [
Expand All @@ -32,7 +31,7 @@ const Sidebar = ({ isSidebarOpen, closeSidebar }: SidebarProps) => {
];

return (
<S.Sidebar $isOpen={isSidebarOpen}>
<S.Sidebar>
<S.Top>
<button>
<S.LogoIcon src={LogoIcon} alt="로고" />
Expand Down
10 changes: 1 addition & 9 deletions frontend/src/components/layouts/Sidebar/styles.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import styled from '@emotion/styled';

interface SidebarProps {
$isOpen: boolean;
}
export const Sidebar = styled.div<SidebarProps>`
export const Sidebar = styled.div`
position: fixed;
z-index: ${({ theme }) => theme.zIndex.sidebar};
left: 0;
transform: translateX(${(props) => (props.$isOpen ? 0 : '-100%')});
display: flex;
flex-direction: column;
Expand All @@ -20,8 +14,6 @@ export const Sidebar = styled.div<SidebarProps>`
filter: drop-shadow(0.25rem 0.25rem 0.25rem lightgrey);
border-radius: 0 1rem 1rem 0;
transition: transform 1s ease-in-out;
@media screen and (max-width: ${({ theme }) => theme.breakpoints.mobile}) {
width: ${({ theme }) => theme.sidebarWidth.mobile};
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useSidebar } from './useSidebar';
31 changes: 31 additions & 0 deletions frontend/src/hooks/useSidebar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useState } from 'react';

const useSidebar = () => {
const CLOSE_TIME = 1000;
const OPEN_TIME = 0.5;

const [isSidebarModalOpen, setIsSidebarModalOpen] = useState(false);
const [isSidebarHidden, setIsSidebarHidden] = useState(true);

const closeSidebar = () => {
setIsSidebarHidden(true);
setTimeout(() => {
setIsSidebarModalOpen(false);
}, CLOSE_TIME);
};

const openSidebar = () => {
setIsSidebarModalOpen(true);
setTimeout(() => {
setIsSidebarHidden(false);
}, OPEN_TIME);
};
return {
isSidebarHidden,
isSidebarModalOpen,
closeSidebar,
openSidebar,
};
};

export default useSidebar;
4 changes: 2 additions & 2 deletions frontend/src/styles/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ export const colors: ThemeProperty<CSSProperties['color']> = {
lightGray: '#F1F2F4',
placeholder: '#D3D3D3',
gray: '#7F7F7F',
sidebarBackground: '#F5F5F5',
sidebarBackground: `rgba(0, 0, 0, 0.25)`,
disabled: '#D8D8D8',
disabledText: '#7F7F7F',
};

export const zIndex: ThemeProperty<CSSProperties['zIndex']> = {
sidebar: 999,
modal: 999,
};

const theme: Theme = {
Expand Down

0 comments on commit b43b0b5

Please sign in to comment.