This repository has been archived by the owner on Jul 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FE] feat:
Notion
으로 부터 업로드 할 수 있는 기능 구현 (#166)
* feat: 노션 글 업로드 API 추가 * feat: `Modal` 컴포넌트 구현 * feat: `useModal` 훅 구현 * refactor: 아이콘 이름 수정 * test: `Modal` 스토리 작성 * test: `FileUploader` 스토리 작성 * feat: `FileUploader` 컴포넌트 구현 * refactor: `useFileUpload` 훅 수정 * feat: `useFileDragAndDrop` 훅 구현 * feat: Add post 버튼 클릭 시 모달이 띄워지도록 수정 * feat: 가져오기 아이콘 추가 * feat: `FileUploadModal` 구현 * refactor: 헤더 크기 시안에 맞게 수정 * feat: 파일 업로드 시 카테고리 아이디 추가 * refactor: 모달 backdrop 상수화 * refactor: 불필요한 함수 인자 제거 및 css inset 수정 * design: `Input` 컴포넌트 placeholder 색상 변경 * refactor: `Input` 컴포넌트로 기존의 노션 인풋 교체 * refactor: 테스트를 위한 불필요한 버튼 제거 * fix: 카테고리 API에 headers content type 추가 * fix: 글 업로드 시 categoryId 넘기는 방식 수정
- Loading branch information
1 parent
3971a56
commit a9b29a4
Showing
20 changed files
with
532 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
frontend/src/components/@common/FileUploader/FileUploader.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import FileUploader from './FileUploader'; | ||
|
||
const meta: Meta<typeof FileUploader> = { | ||
title: 'common/FileUploader', | ||
component: FileUploader, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Playground: Story = { | ||
render: () => { | ||
const onFileSelect = (file: FormData | null) => { | ||
if (file) alert('파일이 선택되었습니다!'); | ||
}; | ||
return <FileUploader onFileSelect={onFileSelect} />; | ||
}, | ||
}; |
77 changes: 77 additions & 0 deletions
77
frontend/src/components/@common/FileUploader/FileUploader.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { InputHTMLAttributes, useEffect } from 'react'; | ||
import { css, styled } from 'styled-components'; | ||
import { useFileUpload } from 'hooks/useFileUpload'; | ||
import { useFileDragAndDrop } from 'hooks/@common/useFileDragAndDrop'; | ||
import { ImportIcon } from 'assets/icons'; | ||
|
||
type Props = { | ||
accept?: InputHTMLAttributes<HTMLInputElement>['accept']; | ||
width?: string; | ||
height?: string; | ||
onFileSelect: (file: FormData | null) => void; | ||
}; | ||
|
||
const FileUploader = ({ accept = '*', width = '30rem', height = '10rem', onFileSelect }: Props) => { | ||
const { onFileChange, openFinder, selectedFile } = useFileUpload(accept); | ||
const { dragRef, isDragging } = useFileDragAndDrop({ onFileChange }); | ||
|
||
useEffect(() => { | ||
onFileSelect(selectedFile); | ||
}, [selectedFile]); | ||
|
||
return ( | ||
<button ref={dragRef} onClick={openFinder}> | ||
<S.Description $isDragging={isDragging} $width={width} $height={height}> | ||
<ImportIcon /> | ||
드래그하거나 클릭해서 업로드 | ||
</S.Description> | ||
</button> | ||
); | ||
}; | ||
|
||
export default FileUploader; | ||
|
||
const S = { | ||
Description: styled.div<{ $isDragging: boolean; $width: string; $height: string }>` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
gap: 1rem; | ||
${({ $width, $height }) => { | ||
return css` | ||
width: ${$width}; | ||
height: ${$height}; | ||
`; | ||
}}; | ||
border: 2px dashed ${({ theme }) => theme.color.gray6}; | ||
background-color: ${({ theme }) => theme.color.gray4}; | ||
font-size: 1.3rem; | ||
color: ${({ theme }) => theme.color.gray7}; | ||
transition: all 0.2s ease-in-out; | ||
${({ $isDragging, theme }) => { | ||
return ( | ||
$isDragging && | ||
css` | ||
border: 2px dashed ${theme.color.primary}; | ||
background-color: ${theme.color.gray5}; | ||
` | ||
); | ||
}} | ||
&:hover { | ||
background-color: ${({ theme }) => theme.color.gray5}; | ||
} | ||
`, | ||
SpinnerWrapper: styled.div<{ $width: string; $height: string }>` | ||
${({ $width, $height }) => { | ||
return css` | ||
width: ${$width}; | ||
height: ${$height}; | ||
`; | ||
}}; | ||
display: flex; | ||
justify-content: center; | ||
`, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import Modal from './Modal'; | ||
import Button from 'components/@common/Button/Button'; | ||
import { useModal } from 'hooks/@common/useModal'; | ||
import { styled } from 'styled-components'; | ||
|
||
const meta = { | ||
title: 'common/Modal', | ||
component: Modal, | ||
argTypes: { | ||
children: { | ||
control: false, | ||
}, | ||
isOpen: { | ||
control: false, | ||
}, | ||
}, | ||
args: { | ||
isOpen: false, | ||
}, | ||
} satisfies Meta<typeof Modal>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Playground: Story = { | ||
render: ({ ...rest }) => { | ||
const { isOpen, openModal, closeModal } = useModal(); | ||
return ( | ||
<> | ||
<Button variant='secondary' onClick={openModal}> | ||
Open Modal | ||
</Button> | ||
<Modal {...rest} isOpen={isOpen} closeModal={closeModal}> | ||
<ModalContent> | ||
<h1>모달</h1> | ||
<p>내용을 마음껏 써주세요.</p> | ||
</ModalContent> | ||
</Modal> | ||
</> | ||
); | ||
}, | ||
}; | ||
|
||
const ModalContent = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
height: 30rem; | ||
p { | ||
margin-top: 13rem; | ||
font-size: 1.3rem; | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { useCallback, useEffect } from 'react'; | ||
import { ComponentPropsWithoutRef } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
import { styled } from 'styled-components'; | ||
import { CloseIcon } from 'assets/icons'; | ||
|
||
type Props = { | ||
isOpen: boolean; | ||
closeModal: () => void; | ||
} & ComponentPropsWithoutRef<'dialog'>; | ||
|
||
const Modal = ({ isOpen = true, closeModal, children, ...rest }: Props) => { | ||
const onKeyDownEscape = useCallback( | ||
(event: KeyboardEvent) => { | ||
if (event.key !== 'Escape') return; | ||
closeModal(); | ||
}, | ||
[closeModal], | ||
); | ||
|
||
useEffect(() => { | ||
if (isOpen) { | ||
window.addEventListener('keydown', onKeyDownEscape); | ||
document.body.style.overflow = 'hidden'; | ||
} | ||
|
||
return () => { | ||
window.removeEventListener('keydown', onKeyDownEscape); | ||
document.body.style.overflow = 'auto'; | ||
}; | ||
}, [isOpen, onKeyDownEscape]); | ||
|
||
return createPortal( | ||
<S.ModalWrapper> | ||
{isOpen && ( | ||
<> | ||
<S.Backdrop onClick={closeModal} /> | ||
<S.Content {...rest}> | ||
<S.CloseButton type='button' onClick={closeModal}> | ||
<CloseIcon width={24} height={24} /> | ||
</S.CloseButton> | ||
{children} | ||
</S.Content> | ||
</> | ||
)} | ||
</S.ModalWrapper>, | ||
document.body, | ||
); | ||
}; | ||
|
||
export default Modal; | ||
|
||
const S = { | ||
ModalWrapper: styled.div` | ||
position: relative; | ||
z-index: 9999; | ||
`, | ||
Backdrop: styled.div` | ||
position: fixed; | ||
inset: 0; | ||
background: ${({ theme }) => theme.color.modalBackdrop}; | ||
`, | ||
Content: styled.dialog` | ||
position: fixed; | ||
inset: 50% auto auto 50%; | ||
display: flex; | ||
justify-content: center; | ||
min-width: 20vw; | ||
max-height: 80vh; | ||
overflow: auto; | ||
padding: 2.5rem; | ||
border: none; | ||
border-radius: 8px; | ||
background-color: ${({ theme }) => theme.color.gray1}; | ||
transform: translate(-50%, -50%); | ||
`, | ||
CloseButton: styled.button` | ||
position: absolute; | ||
inset: 2.5rem 2.5rem auto auto; | ||
`, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.