-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Feature] - 여행기 등록 페이지에 필요한 공통 컴포넌트 구현(리버) (#89)
* feat: MultiImageUpload 컴포넌트 구현 * feat: ThumbnailUpload 컴포넌트 구현 * fix: emotion css를 styled로 수정 - storybook에서 emotion css가 적용되지 않는 문제가 있어서 emotion styled로 수정 * test: ThumnailUpload 컴포넌트 storybook 추가 * feat: MultiImageUpload 컴포넌트 이미지 삭제 기능 구현 * refactor: MultiImageUpload 컴포넌트에서 함수명 코드 컨벤션에 맞게 수정 * feat: ThumbnailUpload 컴포넌트 썸네일 수정 기능 구현 * feat: ThumbnailUploadButton에 gap 수정 * refactor: ThumbnailUpload 컴포넌트에서 중복 제거 * refactor: ThumbnailUpload 컴포넌트에서 화살표 함수로 단순화 * fix: MulitiImageUpload 컴포넌트에서 emotion css를 emotion styled로 수정 - storybook에서 emotion css 적용 되지 않는 이슈때문에 수정 * refactor: 이미지 업로드 로직을 useImageUpload로 분리 * test: MultiImageUpload 컴포넌트 storybook 추가 * fix: ThumbnailUpload 컴포넌트 storybook title 수정 * fix: MultiImageUpload 컴포넌트 storybook title 수정 * refactor: ThumbnailUpload 컴포넌트 storybook에서 base64를 mageUrl로 수정 -CORS 정책때문에 외부 URL에서 이미지를 직접 가져오는 것이 안됐었다. - 이 때문에 imageUrl 대신 base64를 사용했었다 - base64 값이 너무 길다 - 때문에 무료 CORS 프록시 서비를 사용하여 imageUrl을 사용하는 방법으로 수정 * feat: MultiImageUpload 컴포넌트의 이미지 렌더링 부분에 스크롤 추가 * test: MultiImageUpload 컴포넌트 storybook에 이미지 많이 첨부한 경우 추가 * refactor: MultiImageUpload 컴포넌트 삭제 버튼에 svg 사용 * refactor: MultiImageUpload 컴포넌트에서 styled 컴포넌트명 수정 * feat: useDragScroll hook 구현 * feat: MultiImageUpload 컴포넌트에 드래그 스크롤 기능 추가 * feat: MultiImageUpload 컴포넌트에 y축 스크롤 hidden 추가 * refactor: MultiImageUpload 컴포넌트에서 사진 추가 버튼 UI 수정 --------- Co-authored-by: jinyoung <[email protected]>
- Loading branch information
1 parent
64a8dd3
commit 51f1960
Showing
12 changed files
with
649 additions
and
0 deletions.
There are no files selected for viewing
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions
97
frontend/src/components/common/MultiImageUpload/MultiImageUpload.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,97 @@ | ||
import React from "react"; | ||
|
||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import MultiImageUpload from "./MultiImageUpload"; | ||
|
||
const meta = { | ||
title: "common/MultiImageUpload", | ||
component: MultiImageUpload, | ||
decorators: [ | ||
(Story, context) => { | ||
return ( | ||
<div style={{ width: "48rem" }}> | ||
<Story args={{ ...context.args }} /> | ||
</div> | ||
); | ||
}, | ||
], | ||
} satisfies Meta<typeof MultiImageUpload>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = {}; | ||
|
||
export const WithImages: Story = { | ||
decorators: [ | ||
(Story) => { | ||
React.useEffect(() => { | ||
const fetchAndCreateFile = async () => { | ||
const imageUrl = | ||
"https://api.allorigins.win/raw?url=https://i.pinimg.com/474x/df/6d/1a/df6d1a665685af3c0eb7e4c6a0c40169.jpg"; | ||
const file = new File([await fetch(imageUrl).then((r) => r.blob())], "example.png", { | ||
type: "image/png", | ||
}); | ||
|
||
const files = Array(3).fill(file); | ||
|
||
const dataTransfer = new DataTransfer(); | ||
|
||
files.map((file) => dataTransfer.items.add(file)); | ||
|
||
const inputElement = document.querySelector('input[type="file"]') as HTMLInputElement; | ||
if (inputElement) { | ||
Object.defineProperty(inputElement, "files", { | ||
value: dataTransfer.files, | ||
}); | ||
|
||
const event = new Event("change", { bubbles: true }); | ||
inputElement.dispatchEvent(event); | ||
} | ||
}; | ||
|
||
fetchAndCreateFile(); | ||
}, []); | ||
|
||
return <Story />; | ||
}, | ||
], | ||
}; | ||
|
||
export const WithManyImages: Story = { | ||
decorators: [ | ||
(Story) => { | ||
React.useEffect(() => { | ||
const fetchAndCreateFile = async () => { | ||
const imageUrl = | ||
"https://api.allorigins.win/raw?url=https://i.pinimg.com/474x/df/6d/1a/df6d1a665685af3c0eb7e4c6a0c40169.jpg"; | ||
const file = new File([await fetch(imageUrl).then((r) => r.blob())], "example.png", { | ||
type: "image/png", | ||
}); | ||
|
||
const files = Array(7).fill(file); | ||
|
||
const dataTransfer = new DataTransfer(); | ||
|
||
files.map((file) => dataTransfer.items.add(file)); | ||
|
||
const inputElement = document.querySelector('input[type="file"]') as HTMLInputElement; | ||
if (inputElement) { | ||
Object.defineProperty(inputElement, "files", { | ||
value: dataTransfer.files, | ||
}); | ||
|
||
const event = new Event("change", { bubbles: true }); | ||
inputElement.dispatchEvent(event); | ||
} | ||
}; | ||
|
||
fetchAndCreateFile(); | ||
}, []); | ||
|
||
return <Story />; | ||
}, | ||
], | ||
}; |
138 changes: 138 additions & 0 deletions
138
frontend/src/components/common/MultiImageUpload/MultiImageUpload.styled.ts
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,138 @@ | ||
import styled from "@emotion/styled"; | ||
|
||
export const MultiImageUploadContainer = styled.div` | ||
display: flex; | ||
width: 100%; | ||
justify-content: flex-start; | ||
align-items: center; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
`; | ||
|
||
export const MultiImageUploadButton = styled.button` | ||
display: flex; | ||
width: 100%; | ||
padding: ${(props) => props.theme.spacing.s}; | ||
border: 1px solid ${(props) => props.theme.colors.border}; | ||
justify-content: center; | ||
align-items: center; | ||
gap: ${(props) => props.theme.spacing.xs}; | ||
${(props) => props.theme.typography.mobile.detailBold}; | ||
color: ${(props) => props.theme.colors.text.secondary}; | ||
border-radius: 0.4rem; | ||
`; | ||
|
||
export const MultiImageUploadPictureContainer = styled.div` | ||
display: flex; | ||
align-items: center; | ||
flex: 1; | ||
width: 100%; | ||
overflow-x: auto-scroll; | ||
justify-content: flex-start; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
`; | ||
|
||
export const MultiImageUploadPictureWrapper = styled.div` | ||
display: flex; | ||
position: relative; | ||
justify-content: flex-start; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
`; | ||
|
||
export const MultiImageUploadPicture = styled.img` | ||
width: 6rem; | ||
height: 6rem; | ||
object-fit: cover; | ||
object-position: center; | ||
border-radius: 0.4rem; | ||
`; | ||
|
||
export const MultiImageUploadPicturesInfo = styled.div` | ||
display: flex; | ||
width: 6rem; | ||
height: 6rem; | ||
border-radius: 0.4rem; | ||
justify-content: center; | ||
align-items: center; | ||
border: 1px solid ${(props) => props.theme.colors.border}; | ||
flex-direction: column; | ||
gap: ${(props) => props.theme.spacing.s}; | ||
p { | ||
${(props) => props.theme.typography.mobile.detailBold} | ||
color: ${(props) => props.theme.colors.text.secondary} | ||
} | ||
`; | ||
|
||
export const MultiImageUploadPictureAddButton = styled.button<{ $hasPicture: boolean }>` | ||
display: flex; | ||
width: 6rem; | ||
height: 6rem; | ||
margin-top: ${({ $hasPicture }) => ($hasPicture ? "0" : "0.5rem")}; | ||
border-radius: 0.4rem; | ||
justify-content: center; | ||
align-items: center; | ||
border: 1px solid ${(props) => props.theme.colors.border}; | ||
flex-direction: column; | ||
gap: ${(props) => props.theme.spacing.s}; | ||
p { | ||
${(props) => props.theme.typography.mobile.detailBold} | ||
color: ${(props) => props.theme.colors.text.secondary} | ||
} | ||
svg { | ||
width: 2rem; | ||
} | ||
`; | ||
|
||
export const MultiImageUploadDeleteButton = styled.button` | ||
display: flex; | ||
position: absolute; | ||
top: -1rem; | ||
right: -1rem; | ||
justify-content: center; | ||
align-items: center; | ||
width: 2rem; | ||
height: 2rem; | ||
border: 1px solid ${(props) => props.theme.colors.border}; | ||
background-color: #fff; | ||
border-radius: 50%; | ||
svg { | ||
width: 0.8rem; | ||
} | ||
`; | ||
|
||
export const MultiImageUploadHiddenInput = styled.input` | ||
display: none; | ||
`; | ||
|
||
export const ImageScrollContainer = styled.div<{ $isDragging: boolean }>` | ||
display: flex; | ||
overflow: auto hidden; | ||
width: 100%; | ||
height: 7rem; | ||
padding: 1rem 1rem 0 0; | ||
padding-bottom: ${(props) => props.theme.spacing.s}; | ||
flex: 1; | ||
justify-content: flex-start; | ||
align-items: center; | ||
gap: ${(props) => props.theme.spacing.m}; | ||
scrollbar-width: none; | ||
cursor: ${({ $isDragging }) => ($isDragging ? "grab" : "pointer")}; | ||
`; | ||
|
||
export const MultiImageUploadSVGWrapper = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
width: 2.5rem; | ||
height: 2.5rem; | ||
svg { | ||
width: 2rem; | ||
} | ||
`; |
Oops, something went wrong.