e.stopPropagation()}>
게시글 작성
-
+
+
+
세션 선택
+
+
+
+

+
+
+
+
+
@@ -50,72 +105,85 @@ const Modal = ({
-
-
+
+
+ {isDragOver && (
+
+ 파일을 업로드하려면 여기에 놓아주세요.
+
+ )}
+
-
- {selectedFiles && selectedFiles.length > 0 && (
-
-
- {selectedFiles.map((file, index) => (
-
-
- {file.name} ({(file.size / 1024).toFixed(1)} KB)
-
-
-
- ))}
-
- )}
-
-
-
-
-
-
-

+ {selectedFiles && selectedFiles.length > 0 && (
+
+ {selectedFiles.map((file, index) => (
+
+
{file.name}
+
+
+ ))}
-
+ )}
+
-
diff --git a/frontend/src/components/Board/Modal.module.css b/frontend/src/components/Board/Modal.module.css
index af017752..44cd9284 100644
--- a/frontend/src/components/Board/Modal.module.css
+++ b/frontend/src/components/Board/Modal.module.css
@@ -46,6 +46,19 @@
flex-shrink: 0;
}
+.headerRight {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.sessionFieldHeader {
+ min-width: clamp(170px, 24vw, 260px);
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
.title {
font-weight: 700;
font-size: var(--font-lg);
@@ -101,6 +114,13 @@
flex-direction: column;
}
+.sessionLabel {
+ font-weight: 600;
+ font-size: var(--font-sm);
+ color: rgba(23, 23, 23, 1);
+ white-space: nowrap;
+}
+
.label {
font-weight: 700;
font-size: var(--font-md);
@@ -138,9 +158,17 @@
flex-direction: column;
}
+.contentHeader {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--spacing-xs);
+}
+
.contentContainer {
width: 100%;
- min-height: clamp(120px, 20vh, 162px);
+ min-height: clamp(260px, 42vh, 420px);
border-radius: 8px;
border: 0.4px solid rgba(175, 175, 175, 1);
background: rgba(255, 255, 255, 1);
@@ -148,6 +176,29 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
+ position: relative;
+ transition: border-color 0.2s ease, background-color 0.2s ease;
+}
+
+.contentContainerDragOver {
+ border-color: rgba(29, 128, 244, 0.95);
+ background: rgba(237, 245, 255, 1);
+}
+
+.dragOverlay {
+ position: absolute;
+ inset: 0;
+ border: 1px dashed rgba(29, 128, 244, 0.95);
+ border-radius: 8px;
+ background: rgba(237, 245, 255, 0.85);
+ color: rgba(29, 128, 244, 1);
+ font-size: 14px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ z-index: 1;
}
.fileSection {
@@ -156,7 +207,14 @@
gap: 8px;
cursor: pointer;
flex-shrink: 0;
- margin-bottom: var(--spacing-xs);
+ border: 1px solid rgba(215, 215, 215, 1);
+ border-radius: 8px;
+ padding: 6px 10px;
+ user-select: none;
+}
+
+.fileSection:hover {
+ background: rgba(246, 246, 246, 1);
}
.fileText {
@@ -167,50 +225,6 @@
flex-shrink: 0;
}
-.fileList {
- margin-top: 12px;
-}
-
-.fileItem {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- background-color: #f5f5f5;
- border-radius: 4px;
- margin-bottom: 6px;
-}
-
-.fileName {
- font-size: 14px;
- color: #333;
- flex: 1;
- word-break: break-all;
-}
-
-.removeFileButton {
- background: none;
- border: none;
- color: #999;
- font-size: 16px;
- cursor: pointer;
- padding: 0 8px;
- transition: color 0.2s;
- flex-shrink: 0;
-}
-
-.removeFileButton:hover {
- color: #ff4444;
-}
-
-.divider {
- width: 100%;
- height: 0px;
- border: 0.4px solid rgba(175, 175, 175, 1);
- margin-bottom: var(--spacing-xs);
- flex-shrink: 0;
-}
-
.textarea {
border: none;
outline: none;
@@ -227,7 +241,7 @@
line-height: 140%;
color: rgba(23, 23, 23, 1);
background: transparent;
- min-height: clamp(40px, 8vh, 50px);
+ min-height: clamp(200px, 30vh, 320px);
}
.textarea::placeholder {
@@ -237,20 +251,41 @@
color: rgba(170, 170, 170, 1);
}
-.accessField {
- width: 100%;
- max-width: clamp(200px, 40vw, 283px);
+.fileOutsideList {
+ margin-top: 8px;
+ padding-top: 6px;
+}
+
+.fileOutsideItem {
display: flex;
- flex-direction: column;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
}
-.accessLabel {
+.fileInlineName {
+ margin: 0;
+ font-size: 13px;
+ line-height: 1.5;
+ color: rgba(120, 120, 120, 1);
+ word-break: break-all;
+ flex: 1;
+}
+
+.fileRemoveButton {
+ border: none;
+ background: transparent;
+ color: rgba(130, 130, 130, 1);
+ font-size: 12px;
font-weight: 700;
- font-size: var(--font-md);
- line-height: 146%;
- color: rgba(23, 23, 23, 1);
- margin: 0 0 var(--spacing-xs) 0;
- display: block;
+ cursor: pointer;
+ padding: 2px 4px;
+ line-height: 1;
+ flex-shrink: 0;
+}
+
+.fileRemoveButton:hover {
+ color: rgba(231, 76, 60, 1);
}
.selectWrapper {
@@ -329,3 +364,9 @@
.saveButton:active {
transform: translateY(0);
}
+
+.saveButton:disabled {
+ background: rgba(169, 169, 169, 1);
+ cursor: not-allowed;
+ transform: none;
+}
diff --git a/frontend/src/components/Board/PostDetail/FileAttachmentList.jsx b/frontend/src/components/Board/PostDetail/FileAttachmentList.jsx
index 9ee7c296..69492617 100644
--- a/frontend/src/components/Board/PostDetail/FileAttachmentList.jsx
+++ b/frontend/src/components/Board/PostDetail/FileAttachmentList.jsx
@@ -11,6 +11,11 @@ const FileAttachmentList = ({
}) => {
if (!files || files.length === 0) return null;
+ const handleDownload = (file) => {
+ if (isEditMode || !onDownload) return;
+ onDownload(file);
+ };
+
return (
{files.map((file, index) => {
@@ -21,12 +26,24 @@ const FileAttachmentList = ({
: '';
return (
-
+
handleDownload(file)}
+ role={!isEditMode ? 'button' : undefined}
+ tabIndex={!isEditMode ? 0 : undefined}
+ onKeyDown={(e) => {
+ if (isEditMode) return;
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ handleDownload(file);
+ }
+ }}
+ >

!isEditMode && onDownload && onDownload(file)}
/>
{fileName}{' '}
diff --git a/frontend/src/components/Board/PostDetail/PostEditForm.jsx b/frontend/src/components/Board/PostDetail/PostEditForm.jsx
index 3ff7821a..c167d9fc 100644
--- a/frontend/src/components/Board/PostDetail/PostEditForm.jsx
+++ b/frontend/src/components/Board/PostDetail/PostEditForm.jsx
@@ -1,6 +1,6 @@
-import React from 'react';
+import React, { useRef, useState } from 'react';
import styles from '../../../pages/PostDetail.module.css';
-import FileAttachmentList from './FileAttachmentList';
+import FolderIcon from '../../../assets/boardFolder.svg';
const PostEditForm = ({
title,
@@ -14,10 +14,59 @@ const PostEditForm = ({
onAddNewFile,
onSave,
onCancel,
+ isSaving = false,
}) => {
+ const fileInputRef = useRef(null);
+ const [isDragOver, setIsDragOver] = useState(false);
+
+ const handleFileButtonClick = () => {
+ if (fileInputRef.current) {
+ fileInputRef.current.value = '';
+ }
+ fileInputRef.current?.click();
+ };
+
+ const handleFileInputChange = (e) => {
+ onAddNewFile(e);
+
+ // Allow selecting the same file name again by resetting the input value.
+ e.target.value = '';
+ };
+
+ const handleDragEnter = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragOver(true);
+ };
+
+ const handleDragOver = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragOver(true);
+ };
+
+ const handleDragLeave = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (!e.currentTarget.contains(e.relatedTarget)) {
+ setIsDragOver(false);
+ }
+ };
+
+ const handleDrop = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragOver(false);
+
+ const droppedFiles = Array.from(e.dataTransfer.files || []);
+ if (droppedFiles.length > 0) {
+ onAddNewFile({ target: { files: droppedFiles } });
+ }
+ };
+
return (
- {/* 제목 */}
+
- {/* 구분선 */}
-
-
- {/* 내용 */}
-