wr: 파일명 잘리는 현상

This commit is contained in:
2026-04-06 16:12:10 +09:00
parent ff0eeb2e40
commit bd85a3c3aa
2 changed files with 31 additions and 14 deletions
@@ -1,6 +1,7 @@
"use client";
import { CloudUpload, Paperclip } from "lucide-react";
import { FileInput, FileUploader, FileUploaderContent, FileUploaderItem } from "./lib/file_upload";
const dropZoneConfig = {
maxFiles: 5,
maxSize: 1024 * 1024 * 100,
@@ -9,14 +10,19 @@ const dropZoneConfig = {
interface FileUploadProps {
files: File[]; // 파일 배열
setFiles: (files: File[] | null) => void; // 파일 배열 세터
fileTypes?: string; // 옵션 (파일 확장자 문자열)
onFileClick?:(index:number)=> void;
maxFiles?: number; // 파일 클릭 핸들러
files: File[]; // 화면에 표시할 현재 파일 목록
setFiles: (files: File[] | null) => void; // 추가/삭제 후 변경된 파일 목록을 부모에 전달
fileTypes?: string; // 허용할 확장자 문자열(ex: ".pdf,.jpg")
onFileClick?:(index:number)=> void; // 파일명 영역 클릭 시 현재 목록 기준 index 전달
maxFiles?: number; // 업로드 가능한 최대 파일 수
}
/**
* 공통 파일 업로드 UI
* - 실제 파일 상태는 부모가 관리하고, 이 컴포넌트는 목록 표시/추가/삭제 이벤트만 위임한다.
* - 삭제 아이콘 클릭 시 FileUploader 내부에서 index 기준으로 파일을 제외한 배열을 만든 뒤 setFiles로 전달한다.
*/
export const FileUpload =
({ files, setFiles, fileTypes ,onFileClick, maxFiles}: FileUploadProps) => {
if(maxFiles){
@@ -51,6 +57,7 @@ export const FileUpload =
{files&&
files.length > 0 &&
files.map((file:File, i:number) => (
// i는 현재 files 배열 순번이며, 삭제/클릭 이벤트에서 같은 순번값을 사용한다.
<FileUploaderItem key={i} index={i} onFileClick={onFileClick }>
<Paperclip className="h-4 w-4 stroke-current" />
<span className="w-72 truncate">{file.name}</span>
@@ -59,4 +66,4 @@ export const FileUpload =
</FileUploaderContent>
</FileUploader>
)
}
}
@@ -37,7 +37,7 @@ type FileUploaderContextType = {
const FileUploaderContext = createContext<FileUploaderContextType | null>(null);
//현재 value 배열에서 해당 index만 뺀 newFiles 생성
// FileUploader 하위 컴포넌트들이 공통 상태와 핸들러를 사용할 수 있도록 context를 노출한다.
export const useFileUpload = () => {
const context = useContext(FileUploaderContext);
if (!context) {
@@ -55,7 +55,10 @@ type FileUploaderProps = {
};
/**
* File upload Docs: {@link: https://localhost:3000/docs/file-upload}
* 파일 업로드의 공통 동작을 담당하는 컨테이너 컴포넌트
* - 현재 파일 목록(value)을 기준으로 drag & drop / 선택 / 삭제를 처리한다.
* - 실제 파일 상태는 부모가 관리하며, 변경 결과는 onValueChange로만 전달한다.
* - FileInput, FileUploaderContent, FileUploaderItem은 이 context를 통해 같은 상태를 공유한다.
*/
export const FileUploader = forwardRef<
@@ -81,7 +84,7 @@ export const FileUploader = forwardRef<
const [isLOF, setIsLOF] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
// MIME 타입 매핑 함수 추가
// ".pdf,.jpg" 형태의 확장자 문자열을 react-dropzone accept 객체로 변환한다.
const getAcceptObject = (fileTypes?: string): Record<string, string[]> => {
if (!fileTypes) {
return { "image/*": [".jpg", ".jpeg", ".png", ".gif"] };
@@ -132,6 +135,7 @@ export const FileUploader = forwardRef<
const reSelectAll = maxFiles === 1 ? true : reSelect;
const direction: DirectionOptions = dir === "rtl" ? "rtl" : "ltr";
// 현재 value 배열에서 index 위치 파일만 제외한 새 배열을 부모로 전달한다.
const removeFileFromSet = useCallback(
(i: number) => {
if (!value) return;
@@ -141,6 +145,7 @@ export const FileUploader = forwardRef<
[value, onValueChange],
);
// 키보드로 파일 목록 탐색 및 삭제를 지원한다.
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLDivElement>) => {
e.preventDefault();
@@ -197,6 +202,7 @@ export const FileUploader = forwardRef<
[value, activeIndex, removeFileFromSet],
);
// 드롭/선택된 파일을 현재 목록과 합쳐서 부모 상태로 반영한다.
const onDrop = useCallback(
(acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
const files = acceptedFiles;
@@ -239,6 +245,7 @@ export const FileUploader = forwardRef<
[reSelectAll, value],
);
// 최대 파일 수에 도달하면 입력 영역을 비활성화한다.
useEffect(() => {
if (!value) return;
if (value.length === maxFiles) {
@@ -248,6 +255,7 @@ export const FileUploader = forwardRef<
setIsLOF(false);
}, [value, maxFiles]);
// react-dropzone에 넘길 최종 옵션 구성
const opts = dropzoneOptions
? { ...dropzoneOptions, accept: getAcceptObject(acceptName) }
: { accept, maxFiles, maxSize, multiple };
@@ -312,7 +320,7 @@ export const FileUploaderContent = forwardRef<
{...props}
ref={ref}
className={cn(
"w-full flex rounded-xl gap-1",
"w-full flex rounded-xl gap-0",
orientation === "horizontal" ? "flex-raw flex-wrap" : "flex-col",
className,
)}
@@ -336,14 +344,14 @@ export const FileUploaderItem = forwardRef<
ref={ref}
className={cn(
buttonVariants({ variant: "ghost" }),
"h-6 p-1 justify-between cursor-pointer relative w-full",
"h-9 justify-between cursor-pointer relative w-full",
className,
isSelected ? "bg-muted" : "",
)}
{...props}
>
<div className="font-medium leading-none tracking-tight flex items-center gap-1.5 h-full !w-4/5 overflow-hidden "
<div className="font-medium leading-normal tracking-tight flex items-center gap-1.5 h-full !w-4/5 overflow-hidden "
onClick={() =>{onFileClick?.(index)
}}>
{children}
@@ -353,7 +361,9 @@ export const FileUploaderItem = forwardRef<
className={cn(
"absolute",
"!w-1/5 z-50",
direction === "rtl" ? "top-1 left-1" : "top-1 right-1",
direction === "rtl"
? "left-1 top-1/2 -translate-y-1/2"
: "right-1 top-1/2 -translate-y-1/2",
)}
onClick={(e) =>{