Skip to content

Commit

Permalink
feat: 전체 스크롤바 수정&메세지 리스트 동적 이동 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
gogumalatte committed Nov 8, 2024
1 parent 6ff6e75 commit 5d3d43f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 44 deletions.
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

90 changes: 53 additions & 37 deletions src/pages/main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,71 @@ const MainPage = () => {
const [messages, setMessages] = useState<string[]>([]);
const [inputMessage, setInputMessage] = useState("");
const [isSidebarOpen, setSidebarOpen] = useState(false);
const messageListRef = useRef<HTMLDivElement>(null);
const [inputHeight, setInputHeight] = useState(36); // 메시지 입력창 높이 상태 추가
const pageRef = useRef<HTMLDivElement>(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 (
<>
<PageWrapper ref={pageRef}>
<SidebarWrapper isOpen={isSidebarOpen}>
<Sidebar isOpen={isSidebarOpen} toggleSidebar={toggleSidebar} />
</SidebarWrapper>

<Wrapper>
<ContentWrapper ref={messageListRef}>
{messages.length === 0 ? (
<TutorialImageWrapper>
<img src={tutorial} alt="튜토리얼 이미지" />
</TutorialImageWrapper>
) : (
<ContentWrapper inputHeight={inputHeight}>
{messages.length === 0 ? (
<TutorialImageWrapper>
<img src={tutorial} alt="튜토리얼 이미지" />
</TutorialImageWrapper>
) : (
<MessageListWrapper>
<MessageList messages={messages} />
)}
</ContentWrapper>
</MessageListWrapper>
)}
<MessageInputWrapper>
<MessageInput
inputMessage={inputMessage}
setInputMessage={setInputMessage}
onSendMessage={handleSendMessage}
setInputHeight={setInputHeight} // 입력창 높이 업데이트 함수 전달
/>
</MessageInputWrapper>
</Wrapper>
</>
</ContentWrapper>
</PageWrapper>
);
};

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;
Expand All @@ -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%;
Expand Down Expand Up @@ -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; // 메시지 입력창 아래 여백 추가
`;
24 changes: 18 additions & 6 deletions src/pages/main/message-input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ interface MessageInputProps {
inputMessage: string;
setInputMessage: (message: string) => void;
onSendMessage: () => void;
setInputHeight: (height: number) => void; // 추가된 prop
}

export const MessageInput: React.FC<MessageInputProps> = ({
inputMessage,
setInputMessage,
onSendMessage,
setInputHeight, // 추가된 prop
}) => {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const baseURL = import.meta.env.VITE_BASE_URL; // baseURL 가져오기
Expand Down Expand Up @@ -56,6 +58,9 @@ export const MessageInput: React.FC<MessageInputProps> = ({
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;

// 입력창의 높이를 부모 컴포넌트에 전달
setInputHeight(textareaRef.current.scrollHeight);
}
};

Expand Down Expand Up @@ -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)`
Expand All @@ -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;
}
Expand All @@ -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;
`;

0 comments on commit 5d3d43f

Please sign in to comment.