diff --git a/frontend/lib/registry/components/v2-file-upload/FileManagerModal.tsx b/frontend/lib/registry/components/v2-file-upload/FileManagerModal.tsx index de838fbf..3c00aeca 100644 --- a/frontend/lib/registry/components/v2-file-upload/FileManagerModal.tsx +++ b/frontend/lib/registry/components/v2-file-upload/FileManagerModal.tsx @@ -18,7 +18,10 @@ import { Archive, Presentation, X, - Star + Star, + ZoomIn, + ZoomOut, + RotateCcw, } from "lucide-react"; import { formatFileSize } from "@/lib/utils"; import { FileViewerModal } from "./FileViewerModal"; @@ -54,7 +57,12 @@ export const FileManagerModal: React.FC = ({ const [isViewerOpen, setIsViewerOpen] = useState(false); const [selectedFile, setSelectedFile] = useState(null); // 선택된 파일 (좌측 미리보기용) const [previewImageUrl, setPreviewImageUrl] = useState(null); // 이미지 미리보기 URL + const [zoomLevel, setZoomLevel] = useState(1); // 🔍 확대/축소 레벨 + const [imagePosition, setImagePosition] = useState({ x: 0, y: 0 }); // 🖱️ 이미지 위치 + const [isDragging, setIsDragging] = useState(false); // 🖱️ 드래그 중 여부 + const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); // 🖱️ 드래그 시작 위치 const fileInputRef = useRef(null); + const imageContainerRef = useRef(null); // 파일 아이콘 가져오기 const getFileIcon = (fileExt: string) => { @@ -146,6 +154,8 @@ export const FileManagerModal: React.FC = ({ // 파일 클릭 시 미리보기 로드 const handleFileClick = async (file: FileInfo) => { setSelectedFile(file); + setZoomLevel(1); // 🔍 파일 선택 시 확대/축소 레벨 초기화 + setImagePosition({ x: 0, y: 0 }); // 🖱️ 이미지 위치 초기화 // 이미지 파일인 경우 미리보기 로드 // 🔑 점(.)을 제거하고 확장자만 비교 @@ -195,18 +205,50 @@ export const FileManagerModal: React.FC = ({ }; }, [previewImageUrl]); - // 🔑 모달이 열릴 때 첫 번째 파일을 자동으로 선택 + // 🔑 모달이 열릴 때 첫 번째 파일을 자동으로 선택하고 확대/축소 레벨 초기화 React.useEffect(() => { - if (isOpen && uploadedFiles.length > 0 && !selectedFile) { - const firstFile = uploadedFiles[0]; - handleFileClick(firstFile); + if (isOpen) { + setZoomLevel(1); // 🔍 모달 열릴 때 확대/축소 레벨 초기화 + setImagePosition({ x: 0, y: 0 }); // 🖱️ 이미지 위치 초기화 + if (uploadedFiles.length > 0 && !selectedFile) { + const firstFile = uploadedFiles[0]; + handleFileClick(firstFile); + } } }, [isOpen, uploadedFiles, selectedFile]); + // 🖱️ 마우스 드래그 핸들러 + const handleMouseDown = (e: React.MouseEvent) => { + if (zoomLevel > 1) { + setIsDragging(true); + setDragStart({ x: e.clientX - imagePosition.x, y: e.clientY - imagePosition.y }); + } + }; + + const handleMouseMove = (e: React.MouseEvent) => { + if (isDragging && zoomLevel > 1) { + setImagePosition({ + x: e.clientX - dragStart.x, + y: e.clientY - dragStart.y, + }); + } + }; + + const handleMouseUp = () => { + setIsDragging(false); + }; + + // 🔍 확대/축소 레벨이 1로 돌아가면 위치도 초기화 + React.useEffect(() => { + if (zoomLevel <= 1) { + setImagePosition({ x: 0, y: 0 }); + } + }, [zoomLevel]); + return ( <> {}}> - + 파일 관리 ({uploadedFiles.length}개) @@ -267,31 +309,97 @@ export const FileManagerModal: React.FC = ({ )} - {/* 좌우 분할 레이아웃 */} + {/* 좌우 분할 레이아웃 - 좌측 넓게, 우측 고정 너비 */}
- {/* 좌측: 이미지 미리보기 */} -
- {selectedFile && previewImageUrl ? ( - {selectedFile.realFileName} - ) : selectedFile ? ( -
- {getFileIcon(selectedFile.fileExt)} -

미리보기 불가능

+ {/* 좌측: 이미지 미리보기 (확대/축소 가능) */} +
+ {/* 확대/축소 컨트롤 */} + {selectedFile && previewImageUrl && ( +
+ + + {Math.round(zoomLevel * 100)}% + + +
- ) : ( -
- -

파일을 선택하면 미리보기가 표시됩니다

+ )} + + {/* 이미지 미리보기 영역 - 마우스 휠로 확대/축소, 드래그로 이동 */} +
1 ? (isDragging ? 'cursor-grabbing' : 'cursor-grab') : 'cursor-zoom-in' + }`} + onWheel={(e) => { + if (selectedFile && previewImageUrl) { + e.preventDefault(); + const delta = e.deltaY > 0 ? -0.1 : 0.1; + setZoomLevel(prev => Math.min(4, Math.max(0.25, prev + delta))); + } + }} + onMouseDown={handleMouseDown} + onMouseMove={handleMouseMove} + onMouseUp={handleMouseUp} + onMouseLeave={handleMouseUp} + > + {selectedFile && previewImageUrl ? ( + {selectedFile.realFileName} + ) : selectedFile ? ( +
+ {getFileIcon(selectedFile.fileExt)} +

미리보기 불가능

+
+ ) : ( +
+ +

파일을 선택하면 미리보기가 표시됩니다

+
+ )} +
+ + {/* 파일 정보 바 */} + {selectedFile && ( +
+ {selectedFile.realFileName}
)}
- {/* 우측: 파일 목록 */} -
+ {/* 우측: 파일 목록 (고정 너비) */} +