diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index 78a0057..7ed8d68 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -9,60 +9,71 @@ const MainPage = () => { const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const [isSidebarOpen, setSidebarOpen] = useState(false); - const messageListRef = useRef(null); + const [inputHeight, setInputHeight] = useState(36); // 메시지 입력창 높이 상태 추가 + const pageRef = useRef(null); const handleSendMessage = () => { if (inputMessage.trim()) { setMessages((prev) => [inputMessage, ...prev]); - setInputMessage(""); + setInputMessage(""); // 상태가 업데이트된 후에 입력창 초기화 } }; - // 메시지가 추가될 때 메시지 리스트를 아래로 스크롤 + // 메시지가 추가될 때 페이지를 가장 아래로 스크롤 useEffect(() => { - if (messageListRef.current) { - messageListRef.current.scrollTo({ - top: messageListRef.current.scrollHeight, + if (pageRef.current) { + pageRef.current.scrollTo({ + top: pageRef.current.scrollHeight, behavior: "smooth", }); } - }, [messages]); + }, [messages, inputHeight]); // inputHeight 추가하여 메시지 입력창 높이 변경에 따른 스크롤 업데이트 const toggleSidebar = () => { setSidebarOpen((prev) => !prev); }; return ( - <> + - - - {messages.length === 0 ? ( - - 튜토리얼 이미지 - - ) : ( + + {messages.length === 0 ? ( + + 튜토리얼 이미지 + + ) : ( + - )} - + + )} - - + + ); }; export default MainPage; -// SidebarWrapper 스타일 컴포넌트 +// 전체 페이지 래퍼 (스크롤 가능한 요소) +const PageWrapper = styled.div` + position: relative; + height: 100vh; + overflow-y: auto; // 전체 페이지에 스크롤이 생긴도록 설정 + display: flex; + flex-direction: column; +`; + +// SidebarWrapper 스타일 컨텐츠 const SidebarWrapper = styled.div<{ isOpen: boolean }>` position: fixed; left: 0; @@ -75,23 +86,30 @@ const SidebarWrapper = styled.div<{ isOpen: boolean }>` z-index: 1000; `; -// Wrapper 컴포넌트 스타일 -const Wrapper = styled.div` - position: relative; - height: 100vh; // 전체 화면 높이 차지 +// ContentWrapper 스타일 컨텐츠 +const ContentWrapper = styled.div<{ inputHeight: number }>` + flex: 1; display: flex; flex-direction: column; + align-items: center; + justify-content: flex-end; + margin-bottom: ${({ inputHeight }) => + Math.min( + inputHeight - 40, + 120 + )}px; // 메시지 입력창 높이에 따른 여백 동적 조정 + transition: margin-left 0.3s ease-in-out; `; -// ContentWrapper 스타일 컴포넌트 (메시지 리스트를 포함) -const ContentWrapper = styled.div` - width: 95%; - flex: 1; // 남은 공간을 모두 차지하여 MessageInput 위에 위치 - overflow-y: auto; // 메시지 리스트가 길어지면 스크롤 가능하도록 설정 - padding-bottom: 80px; // 메시지 입력창 공간 확보 +// MessageListWrapper 스타일 컨텐츠 (메시지 리스트를 포함) +const MessageListWrapper = styled.div` + width: 100%; + flex-grow: 1; + overflow-y: auto; + margin-bottom: 20px; // 메시지 입력창과 메시지 리스트 간 여백 추가 `; -// TutorialImageWrapper 스타일 컴포넌트 +// TutorialImageWrapper 스타일 컨텐츠 const TutorialImageWrapper = styled.div` position: absolute; top: 40%; @@ -119,13 +137,11 @@ const TutorialImageWrapper = styled.div` } `; -// MessageInputWrapper 스타일 컴포넌트 +// MessageInputWrapper 스타일 컨텐츠 const MessageInputWrapper = styled.div` - position: fixed; - bottom: 20px; - left: 50%; - transform: translateX(-50%); width: 90%; max-width: 800px; - z-index: 100; + margin-top: 20px; + position: relative; + margin-bottom: 20px; // 메시지 입력창 아래 여백 추가 `; diff --git a/src/pages/main/message-input/index.tsx b/src/pages/main/message-input/index.tsx index b85120d..81ca961 100644 --- a/src/pages/main/message-input/index.tsx +++ b/src/pages/main/message-input/index.tsx @@ -8,12 +8,14 @@ interface MessageInputProps { inputMessage: string; setInputMessage: (message: string) => void; onSendMessage: () => void; + setInputHeight: (height: number) => void; // 추가된 prop } export const MessageInput: React.FC = ({ inputMessage, setInputMessage, onSendMessage, + setInputHeight, // 추가된 prop }) => { const textareaRef = useRef(null); const baseURL = import.meta.env.VITE_BASE_URL; // baseURL 가져오기 @@ -56,6 +58,9 @@ export const MessageInput: React.FC = ({ if (textareaRef.current) { textareaRef.current.style.height = "auto"; textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; + + // 입력창의 높이를 부모 컴포넌트에 전달 + setInputHeight(textareaRef.current.scrollHeight); } }; @@ -106,7 +111,7 @@ const MessageInputContainer = styled(Box)` const TextareaWrapper = styled.div` position: relative; display: flex; - align-items: center; + align-items: flex-end; `; const StyledTextarea = styled(Textarea)` @@ -115,12 +120,20 @@ const StyledTextarea = styled(Textarea)` outline: none; width: 100%; font-size: 16px; - padding-right: 40px; + padding-right: 45px; /* 전송 버튼과의 간격을 넓히기 위해 여유 공간 추가 */ resize: none; - overflow-y: auto; // 스크롤바 추가 - max-height: 200px; // 최대 높이 설정 + overflow-y: auto; + max-height: 180px; /* 높이 유지 */ min-height: 36px; line-height: 1.5; + scrollbar-width: thin; /* 스크롤바의 너비를 줄임 (Firefox 지원) */ + &::-webkit-scrollbar { + width: 6px; /* 스크롤바의 너비를 줄임 (Chrome, Safari 지원) */ + } + &::-webkit-scrollbar-thumb { + background-color: #ccc; /* 스크롤바의 색상 */ + border-radius: 10px; /* 스크롤바의 모서리를 둥글게 */ + } ::placeholder { color: #ccc; } @@ -132,8 +145,7 @@ const StyledTextarea = styled(Textarea)` const FixedIconButton = styled(IconButton)` position: absolute; - right: -10px; - bottom: -5px; + right: 15px; /* 버튼을 입력창 안쪽에 정확하게 배치 */ background-color: #d7d7d7; z-index: 10; `;