From a6aa65d509739bcdee3a4a1e199f454e63b7bebc Mon Sep 17 00:00:00 2001 From: y0unj1NoH Date: Thu, 28 Nov 2024 14:07:39 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20PostImageManager=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/molecules/index.ts | 1 + .../organisms/PostImageManager/index.tsx | 50 +++++++++++++++++ .../organisms/PostImageManager/stories.tsx | 54 +++++++++++++++++++ .../organisms/PostImageManager/styled.ts | 12 +++++ src/components/organisms/index.ts | 1 + 5 files changed, 118 insertions(+) create mode 100644 src/components/organisms/PostImageManager/index.tsx create mode 100644 src/components/organisms/PostImageManager/stories.tsx create mode 100644 src/components/organisms/PostImageManager/styled.ts diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts index 88ff46af..9dfc3303 100644 --- a/src/components/molecules/index.ts +++ b/src/components/molecules/index.ts @@ -11,3 +11,4 @@ export { TabItem } from "./TabItem"; export { TabMenu } from "./TabMenu"; export { TextDividerList } from "./TextDividerList"; export { TextWithImage } from "./TextWithImage"; +export { UploadedImageCounter } from "./UploadedImageCounter"; diff --git a/src/components/organisms/PostImageManager/index.tsx b/src/components/organisms/PostImageManager/index.tsx new file mode 100644 index 00000000..ab33fc1e --- /dev/null +++ b/src/components/organisms/PostImageManager/index.tsx @@ -0,0 +1,50 @@ +import { Fragment, useEffect, useState } from "react"; +import { UploadedImageCounter } from "components/molecules"; +import { PostImageItem } from "components/organisms"; + +import { PostImageManagerWrapper, PostImageListWrapper } from "./styled"; + +interface IPostImageManagerProps { + imgUrls: string[]; + setImgUrls: React.Dispatch>; +} + +export const PostImageManager = ({ + imgUrls, + setImgUrls +}: IPostImageManagerProps) => { + const onChange = (file: File) => { + const reader = new FileReader(); + reader.onload = (e) => { + const url = e.target?.result; + if (typeof url === "string") { + setImgUrls((prev) => [...prev, url]); + } + }; + reader.readAsDataURL(file); + }; + + return ( + + + + {imgUrls.map((url, index) => ( + + { + setImgUrls((prev) => prev.filter((_, i) => i !== index)); + }} + /> + + ))} + + + ); +}; + diff --git a/src/components/organisms/PostImageManager/stories.tsx b/src/components/organisms/PostImageManager/stories.tsx new file mode 100644 index 00000000..3dc894f2 --- /dev/null +++ b/src/components/organisms/PostImageManager/stories.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { PostImageManager } from "."; + +const meta: Meta = { + title: "Organisms/PostImageManager", + component: PostImageManager, + tags: ["autodocs"], + argTypes: { + imgUrls: { + control: { + type: "object" + }, + description: "이미지 URL 배열" + }, + setImgUrls: { + action: "setImgUrls", + description: "이미지 URL 배열을 설정하는 함수" + } + } +}; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => { + const Component = () => { + const [imgUrls, setImgUrls] = useState([]); + return ; + }; + return ; + } +}; + +export const ImageExist: Story = { + args: { + imgUrls: [ + "https://github.com/moypp.png", + "https://github.com/ppyom.png", + "https://github.com/moypp.png", + "https://github.com/ppyom.png" + ] + }, + render: (args) => { + const Component = () => { + const [imgUrls, setImgUrls] = useState(args.imgUrls); + return ; + }; + return ; + } +}; + +export default meta; + diff --git a/src/components/organisms/PostImageManager/styled.ts b/src/components/organisms/PostImageManager/styled.ts new file mode 100644 index 00000000..33ac0298 --- /dev/null +++ b/src/components/organisms/PostImageManager/styled.ts @@ -0,0 +1,12 @@ +import styled from "@emotion/styled"; + +export const PostImageManagerWrapper: ReturnType< + typeof styled.div +> = styled.div` + display: flex; +`; + +export const PostImageListWrapper: ReturnType = styled.div` + display: flex; +`; + diff --git a/src/components/organisms/index.ts b/src/components/organisms/index.ts index dc66fd23..a749c25c 100644 --- a/src/components/organisms/index.ts +++ b/src/components/organisms/index.ts @@ -1,2 +1,3 @@ export { Map } from "./Map"; +export { PostImageItem } from "./PostImageItem"; export { SearchHistoryItem } from "./SearchHistoryItem"; From 1ca6372cf3f13cc02a9e13760c6f27836f08985d Mon Sep 17 00:00:00 2001 From: y0unj1NoH Date: Thu, 28 Nov 2024 14:12:07 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20useCallback=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organisms/PostImageManager/index.tsx | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/organisms/PostImageManager/index.tsx b/src/components/organisms/PostImageManager/index.tsx index ab33fc1e..2d6ec22f 100644 --- a/src/components/organisms/PostImageManager/index.tsx +++ b/src/components/organisms/PostImageManager/index.tsx @@ -1,4 +1,4 @@ -import { Fragment, useEffect, useState } from "react"; +import { Fragment, useCallback } from "react"; import { UploadedImageCounter } from "components/molecules"; import { PostImageItem } from "components/organisms"; @@ -13,16 +13,26 @@ export const PostImageManager = ({ imgUrls, setImgUrls }: IPostImageManagerProps) => { - const onChange = (file: File) => { - const reader = new FileReader(); - reader.onload = (e) => { - const url = e.target?.result; - if (typeof url === "string") { - setImgUrls((prev) => [...prev, url]); - } - }; - reader.readAsDataURL(file); - }; + const onChange = useCallback( + (file: File) => { + const reader = new FileReader(); + reader.onload = (e) => { + const url = e.target?.result; + if (typeof url === "string") { + setImgUrls((prev) => [...prev, url]); + } + }; + reader.readAsDataURL(file); + }, + [setImgUrls] + ); + + const handleRemoveImage = useCallback( + (index: number) => { + setImgUrls((prev) => prev.filter((_, i) => i !== index)); + }, + [setImgUrls] + ); return ( @@ -37,9 +47,7 @@ export const PostImageManager = ({ { - setImgUrls((prev) => prev.filter((_, i) => i !== index)); - }} + onClick={() => handleRemoveImage(index)} /> ))} From 19043fc6fdf021104d88612170441d029b8ad7c3 Mon Sep 17 00:00:00 2001 From: y0unj1NoH Date: Thu, 28 Nov 2024 15:33:43 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20ImageInfo=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20props=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organisms/PostImageManager/index.tsx | 36 +++++++++------- .../organisms/PostImageManager/stories.tsx | 42 ++++++++++++------- src/types/image.d.ts | 5 +++ src/types/index.ts | 1 + 4 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 src/types/image.d.ts diff --git a/src/components/organisms/PostImageManager/index.tsx b/src/components/organisms/PostImageManager/index.tsx index 2d6ec22f..d3ab38b5 100644 --- a/src/components/organisms/PostImageManager/index.tsx +++ b/src/components/organisms/PostImageManager/index.tsx @@ -1,51 +1,57 @@ -import { Fragment, useCallback } from "react"; +import { Fragment, useCallback, useEffect } from "react"; import { UploadedImageCounter } from "components/molecules"; import { PostImageItem } from "components/organisms"; import { PostImageManagerWrapper, PostImageListWrapper } from "./styled"; +import { ImageInfo } from "types"; interface IPostImageManagerProps { - imgUrls: string[]; - setImgUrls: React.Dispatch>; + /** ImageInfo 배열 (url: S3에 업로드 된 이미지 url, base64Url: 아직 S3에 올라가지 않아서 미리보기만 제공되는 url, file: 아직 안올라간 이미지들을 나중에 S3에 올리기 위해 필요한 file)*/ + imageInfos: ImageInfo[]; + /** imageInfos를 설정하는 함수 */ + setImageInfos: React.Dispatch>; } export const PostImageManager = ({ - imgUrls, - setImgUrls + imageInfos, + setImageInfos }: IPostImageManagerProps) => { const onChange = useCallback( (file: File) => { const reader = new FileReader(); reader.onload = (e) => { - const url = e.target?.result; - if (typeof url === "string") { - setImgUrls((prev) => [...prev, url]); - } + const base64Url = e.target?.result as string; + const newImgData: ImageInfo = { + url: "", + base64Url, + file + }; + setImageInfos((prev) => [...prev, newImgData]); }; reader.readAsDataURL(file); }, - [setImgUrls] + [setImageInfos] ); const handleRemoveImage = useCallback( (index: number) => { - setImgUrls((prev) => prev.filter((_, i) => i !== index)); + setImageInfos((prev) => prev.filter((_, i) => i !== index)); }, - [setImgUrls] + [setImageInfos] ); return ( - {imgUrls.map((url, index) => ( + {imageInfos.map((info, index) => ( handleRemoveImage(index)} /> diff --git a/src/components/organisms/PostImageManager/stories.tsx b/src/components/organisms/PostImageManager/stories.tsx index 3dc894f2..9c677df9 100644 --- a/src/components/organisms/PostImageManager/stories.tsx +++ b/src/components/organisms/PostImageManager/stories.tsx @@ -1,21 +1,23 @@ import { useState } from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { PostImageManager } from "."; +import { ImageInfo } from "types"; const meta: Meta = { title: "Organisms/PostImageManager", component: PostImageManager, tags: ["autodocs"], argTypes: { - imgUrls: { + imageInfos: { control: { type: "object" }, - description: "이미지 URL 배열" + description: + "ImageInfo 배열 (url: S3에 업로드 된 이미지 url, base64Url: 아직 S3에 올라가지 않아서 미리보기만 제공되는 url, file: 아직 안올라간 이미지들을 나중에 S3에 올리기 위해 필요한 file)" }, - setImgUrls: { - action: "setImgUrls", - description: "이미지 URL 배열을 설정하는 함수" + setImageInfos: { + action: "setImageInfos", + description: "imageInfos를 설정하는 함수" } } }; @@ -25,8 +27,13 @@ type Story = StoryObj; export const Default: Story = { render: (args) => { const Component = () => { - const [imgUrls, setImgUrls] = useState([]); - return ; + const [imageInfos, setImageInfos] = useState([]); + return ( + + ); }; return ; } @@ -34,17 +41,24 @@ export const Default: Story = { export const ImageExist: Story = { args: { - imgUrls: [ - "https://github.com/moypp.png", - "https://github.com/ppyom.png", - "https://github.com/moypp.png", - "https://github.com/ppyom.png" + imageInfos: [ + { url: "https://github.com/moypp.png", base64Url: "", file: undefined }, + { url: "https://github.com/ppyom.png", base64Url: "", file: undefined }, + { url: "https://github.com/moypp.png", base64Url: "", file: undefined }, + { url: "https://github.com/ppyom.png", base64Url: "", file: undefined } ] }, render: (args) => { const Component = () => { - const [imgUrls, setImgUrls] = useState(args.imgUrls); - return ; + const [imageInfos, setImageInfos] = useState( + args.imageInfos + ); + return ( + + ); }; return ; } diff --git a/src/types/image.d.ts b/src/types/image.d.ts new file mode 100644 index 00000000..9cf84651 --- /dev/null +++ b/src/types/image.d.ts @@ -0,0 +1,5 @@ +export interface ImageInfo { + url?: string; + base64Url?: string; + file?: File; +} diff --git a/src/types/index.ts b/src/types/index.ts index 25306760..89907287 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,5 @@ export * from "./icon"; +export * from "./image"; export * from "./map"; export * from "./selectOption"; export * from "./navMenu";