"use client"; import { useCallback, useRef, useState } from "react"; interface Props { file: File | null; onFile: (f: File | null) => void; } /* 고해상도 원본 수급이 어려운 현실을 반영해 해상도는 차단하지 않는다. 낮으면 알려만 주고 업로드는 그대로 진행시킨다. */ const SOFT_LONG_EDGE = 1000; export default function PosterDropzone({ file, onFile }: Props) { const inputRef = useRef(null); const [preview, setPreview] = useState(null); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); const [dragging, setDragging] = useState(false); const accept = useCallback((f: File) => { if (!/^image\/(jpeg|png|webp|gif)$/.test(f.type)) { setError("JPG / PNG / WEBP / GIF 파일만 올릴 수 있습니다."); return; } setError(null); const url = URL.createObjectURL(f); const img = new Image(); img.onload = () => { const edge = Math.max(img.width, img.height); setNotice(edge < SOFT_LONG_EDGE ? `긴 변 ${edge}px입니다. 그대로 진행할 수 있고, 깊은 줌 구간만 다소 부드럽게 보일 수 있습니다.` : null); setPreview(url); onFile(f); }; img.src = url; }, [onFile]); return (
inputRef.current?.click()} onDragOver={(e) => { e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={(e) => { e.preventDefault(); setDragging(false); const f = e.dataTransfer.files?.[0]; if (f) accept(f); }} style={{ // 포스터는 세로물 — 입력창도 4:5 세로 비율이어야 직관적 aspectRatio: "4 / 5", maxWidth: 400, margin: "0 auto", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", padding: preview ? 0 : "var(--s-5)", textAlign: "center", cursor: "pointer", borderStyle: preview ? "solid" : "dashed", borderColor: dragging ? "var(--color-mint)" : "var(--color-border-gray-700)", background: dragging ? "var(--color-mint-10)" : "rgba(18, 26, 29, 0.4)", }} > {preview ? ( // eslint-disable-next-line @next/next/no-img-element 포스터 미리보기 ) : (

포스터를 끌어다 놓거나
클릭해서 선택

JPG · PNG · WEBP · GIF
해상도 제한 없음

)} { const f = e.target.files?.[0]; if (f) accept(f); }} />
{error && (

{error}

)} {notice && (

{notice}

)} {file && (

{file.name} · {(file.size / 1024 / 1024).toFixed(1)}MB

)}
); }