Skip to content

Commit bc66eae

Browse files
committed
chore: ImageInput 복구
1 parent 59f1711 commit bc66eae

File tree

5 files changed

+132
-1
lines changed

5 files changed

+132
-1
lines changed

public/slogan.png

-90.8 KB
Binary file not shown.

public/testlogo.png

-35.7 KB
Binary file not shown.

public/text_logo.png

-106 KB
Binary file not shown.

src/app/(pages)/(workform)/addform/section/RecruitContentSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use client";
22
import BaseInput from "@/app/components/input/text/BaseInput";
33
import BaseTextArea from "@/app/components/input/textarea/BaseTextArea";
4-
import ImageInput from "@/app/components/input/file/ImageInput/ImageInput";
54
import DatePickerInput from "@/app/components/input/dateTimeDaypicker/DatePickerInput";
65
import { cn } from "@/lib/tailwindUtil";
76
import { useFormContext } from "react-hook-form";
@@ -12,6 +11,7 @@ import useUploadImages from "@/hooks/queries/user/me/useImageUpload";
1211
import { formatToLocaleDate } from "@/utils/formatters";
1312
import DotLoadingSpinner from "@/app/components/loading-spinner/DotLoadingSpinner";
1413
import { isDirty } from "zod";
14+
import ImageInput from "@/app/components/input/file/ImageInput/ImageInput";
1515

1616
// 워크폼 만들기 - 사장님 - 1-모집내용
1717

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"use client";
2+
import { HiUpload } from "react-icons/hi";
3+
import { forwardRef, useState, useEffect } from "react";
4+
import { toast } from "react-hot-toast";
5+
import { DragDropContext, Droppable, Draggable, DropResult } from "react-beautiful-dnd";
6+
import PreviewItem from "./PreviewItem";
7+
import { cn } from "@/lib/tailwindUtil";
8+
import { ImageInputType } from "@/types/addform";
9+
import React from "react";
10+
11+
interface ImageInputProps {
12+
name: string;
13+
onChange?: (files: File[] | string[]) => void;
14+
onDelete?: (id: string) => void;
15+
initialImageList: ImageInputType[];
16+
}
17+
18+
const ImageInput = forwardRef<HTMLInputElement, ImageInputProps>((props, ref) => {
19+
const [imageList, setImageList] = useState<ImageInputType[]>(props.initialImageList || []);
20+
21+
useEffect(() => {
22+
if (props.initialImageList?.length > 0) {
23+
setImageList(props.initialImageList);
24+
}
25+
}, [props.initialImageList]);
26+
27+
const handleFileChange = (selectedFiles: FileList | null) => {
28+
if (selectedFiles) {
29+
const filesArray = Array.from(selectedFiles); // FileList를 배열로 변환
30+
const validFiles = filesArray.filter((file) => file.type.startsWith("image/"));
31+
32+
if (validFiles.length + imageList.length > 3) {
33+
toast.error("이미지는 최대 3개까지 업로드할 수 있습니다.");
34+
return;
35+
}
36+
37+
if (validFiles.length === 0) {
38+
toast.error("이미지 파일만 업로드할 수 있습니다.");
39+
return;
40+
}
41+
42+
// 선택된 파일을 상위 컴포넌트로 전달
43+
props.onChange?.(validFiles);
44+
}
45+
};
46+
47+
const handleDeleteImage = (targetUrl: string) => {
48+
const newImageList = imageList.filter((image) => image.url !== targetUrl);
49+
setImageList(newImageList);
50+
props.onDelete?.(targetUrl);
51+
};
52+
53+
const handleOpenFileSelector = () => {
54+
if (typeof ref === "function") {
55+
const fileInput = document.querySelector(`input[name="${props.name}"]`);
56+
if (fileInput) {
57+
(fileInput as HTMLInputElement).click();
58+
}
59+
} else if (ref && "current" in ref) {
60+
ref.current?.click();
61+
}
62+
};
63+
64+
const colorStyle = {
65+
bgColor: "bg-background-200",
66+
borderColor: "border-[0.5px] border-transparent",
67+
hoverColor: "hover:border-grayscale-200 hover:bg-background-300",
68+
innerHoverColor: "hover:bg-background-300",
69+
};
70+
71+
const handleDragEnd = (result: DropResult) => {
72+
if (!result.destination) return;
73+
74+
const items = Array.from(imageList);
75+
const [reorderedItem] = items.splice(result.source.index, 1);
76+
items.splice(result.destination.index, 0, reorderedItem);
77+
78+
setImageList(items);
79+
// 상위 컴포넌트에 변경된 이미지 URL 배열 전달
80+
props.onChange?.(items.map((item) => item.url));
81+
};
82+
83+
return (
84+
<DragDropContext onDragEnd={handleDragEnd}>
85+
<div className="flex gap-5 lg:gap-6">
86+
<div
87+
onClick={handleOpenFileSelector}
88+
className={cn(
89+
"relative size-20 cursor-pointer rounded-lg lg:size-[116px]",
90+
colorStyle.bgColor,
91+
colorStyle.borderColor,
92+
colorStyle.hoverColor
93+
)}
94+
>
95+
<input
96+
ref={ref}
97+
type="file"
98+
name={props.name}
99+
accept="image/*"
100+
onChange={(e) => handleFileChange(e.target.files)}
101+
className="hidden"
102+
multiple
103+
/>
104+
<div className="pointer-events-none absolute top-0 z-10 p-7 lg:p-10">
105+
<HiUpload className="text-[24px] text-grayscale-400 lg:text-[36px]" />
106+
</div>
107+
</div>
108+
<Droppable droppableId="image-list" direction="horizontal">
109+
{(provided) => (
110+
<div {...provided.droppableProps} ref={provided.innerRef} className="flex gap-5 lg:gap-6">
111+
{imageList.map((image, index) => (
112+
<Draggable key={image.id} draggableId={image.id} index={index}>
113+
{(provided) => (
114+
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
115+
<PreviewItem image={image} handleDeleteImage={handleDeleteImage} placeholder={false} />
116+
</div>
117+
)}
118+
</Draggable>
119+
))}
120+
{provided.placeholder}
121+
</div>
122+
)}
123+
</Droppable>
124+
</div>
125+
</DragDropContext>
126+
);
127+
});
128+
129+
ImageInput.displayName = "ImageInput";
130+
131+
export default ImageInput;

0 commit comments

Comments
 (0)