Skip to content

Commit 4733490

Browse files
committed
feat: 채팅 헤더, 리스트, 질문 섹션 퍼블리싱 및 이전 컴포넌트 수정
1 parent 4256ea7 commit 4733490

File tree

5 files changed

+195
-15
lines changed

5 files changed

+195
-15
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import styled from 'styled-components';
2+
import ThreePointIcon from '@assets/icons/three-point.svg';
3+
import OutIcon from '@assets/icons/out.svg';
4+
5+
interface ChatHeaderProps {
6+
outBtnHandler?: () => void;
7+
}
8+
9+
export const ChatHeader = ({ outBtnHandler }: ChatHeaderProps) => {
10+
return (
11+
<ChatHeaderContainer>
12+
<HeaderBtn onClick={outBtnHandler}>
13+
<StyledIcon as={OutIcon} />
14+
</HeaderBtn>
15+
<h2>채팅</h2>
16+
<HeaderBtn>
17+
<StyledIcon as={ThreePointIcon} />
18+
</HeaderBtn>
19+
</ChatHeaderContainer>
20+
);
21+
};
22+
export default ChatHeader;
23+
24+
const ChatHeaderContainer = styled.div`
25+
display: flex;
26+
justify-content: space-between;
27+
align-items: center;
28+
padding: 15px 15px;
29+
border-top: 1px solid ${({ theme }) => theme.tokenColors['surface-alt']};
30+
color: ${({ theme }) => theme.tokenColors['color-white']};
31+
${({ theme }) => theme.tokenTypographys['display-bold20']};
32+
`;
33+
34+
const HeaderBtn = styled.button`
35+
display: flex;
36+
color: ${({ theme }) => theme.tokenColors['text-bold']};
37+
cursor: pointer;
38+
`;
39+
40+
const StyledIcon = styled.svg`
41+
width: 25px;
42+
height: 25px;
43+
cursor: pointer;
44+
`;

frontend/src/components/chat/ChatInput.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const ChatInput = ({ type }: ChatInputProps) => {
4545
};
4646

4747
return (
48-
<ChatInputContainer $hasInput={hasInput} $isFocused={isFocused}>
48+
<ChatInputWrapper $hasInput={hasInput} $isFocused={isFocused}>
4949
<InputBtn aria-label={type}>
5050
{type === 'normal' ? <StyledIcon as={SpeechBubbleIcon} /> : <StyledIcon as={QuestionIcon} />}
5151
</InputBtn>
@@ -58,25 +58,25 @@ export const ChatInput = ({ type }: ChatInputProps) => {
5858
<InputBtn aria-label="전송">
5959
<StyledIcon as={SendIcon} />
6060
</InputBtn>
61-
</ChatInputContainer>
61+
</ChatInputWrapper>
6262
);
6363
};
6464
export default ChatInput;
6565

66-
const ChatInputContainer = styled.div<{ $hasInput: boolean; $isFocused: boolean }>`
66+
const ChatInputWrapper = styled.div<{ $hasInput: boolean; $isFocused: boolean }>`
6767
min-height: 20px;
6868
display: flex;
69-
padding: 13px 15px;
70-
gap: 16px;
71-
border: 2px solid #373a3f;
69+
padding: 5px 10px;
70+
gap: 10px;
71+
border: 3px solid ${({ theme }) => theme.tokenColors['text-weak']};
7272
border-radius: 7px;
7373
background-color: transparent;
7474
7575
${({ $isFocused, $hasInput }) =>
7676
!$hasInput &&
7777
!$isFocused &&
7878
css`
79-
border: 2px solid transparent;
79+
border: 3px solid transparent;
8080
background-color: #373a3f;
8181
`}
8282
`;
@@ -89,12 +89,14 @@ const ChatInputArea = styled.textarea`
8989
border: none;
9090
outline: none;
9191
color: ${({ theme }) => theme.tokenColors['text-strong']};
92-
${({ theme }) => theme.tokenTypographys['display-medium14']}
92+
${({ theme }) => theme.tokenTypographys['display-medium16']}
9393
background-color: transparent;
9494
`;
9595

9696
const InputBtn = styled.button`
9797
display: flex;
98+
height: 25px;
99+
align-items: center;
98100
color: ${({ theme }) => theme.tokenColors['text-strong']};
99101
cursor: pointer;
100102
`;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import styled from 'styled-components';
2+
import QuestionCard from './QuestionCard';
3+
import { useEffect, useRef } from 'react';
4+
5+
interface ChatListProps {}
6+
7+
const sampleData = [
8+
{ user: '고양이', message: 'ㅇㅅㅇ', type: 'normal' },
9+
{ user: '강아지', message: 'ㅎㅇㅎㅇ', type: 'normal' },
10+
{
11+
user: '오리',
12+
message:
13+
'가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하',
14+
type: 'normal'
15+
}
16+
];
17+
18+
function getRandomBrightColor(): string {
19+
const hue = Math.floor(Math.random() * 360);
20+
const saturation = Math.floor(Math.random() * 50) + 50;
21+
const lightness = Math.floor(Math.random() * 30) + 50;
22+
23+
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
24+
}
25+
26+
export const ChatList = ({}: ChatListProps) => {
27+
const chatListRef = useRef<HTMLDivElement | null>(null);
28+
29+
useEffect(() => {
30+
if (chatListRef.current) {
31+
chatListRef.current.scrollTop = chatListRef.current.scrollHeight;
32+
}
33+
}, []);
34+
35+
return (
36+
<ChatListWrapper ref={chatListRef}>
37+
{[...Array(6)].map((_, i) =>
38+
sampleData.map((chat, index) => (
39+
<ChatItemWrapper key={`${i}-${index}`}>
40+
{chat.type === 'normal' ? (
41+
<NormalChat $pointColor={getRandomBrightColor()}>
42+
<span className="text_point">{chat.user}</span>
43+
<span>{chat.message}</span>
44+
</NormalChat>
45+
) : (
46+
<QuestionCard type="client" user={chat.user} message={chat.message} />
47+
)}
48+
</ChatItemWrapper>
49+
))
50+
)}
51+
</ChatListWrapper>
52+
);
53+
};
54+
export default ChatList;
55+
56+
const ChatListWrapper = styled.div`
57+
max-height: 100%;
58+
display: flex;
59+
flex-direction: column;
60+
overflow-y: auto;
61+
padding: 50px 20px 0 20px;
62+
scrollbar-width: none;
63+
`;
64+
65+
const ChatItemWrapper = styled.div`
66+
margin-top: auto;
67+
padding: 5px 0;
68+
`;
69+
70+
const NormalChat = styled.div<{ $pointColor: string }>`
71+
${({ theme }) => theme.tokenTypographys['display-medium14']};
72+
color: ${({ theme }) => theme.tokenColors['color-white']};
73+
.text_point {
74+
color: ${({ $pointColor }) => $pointColor};
75+
margin-right: 5px;
76+
}
77+
`;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { useState } from 'react';
2+
import styled from 'styled-components';
3+
import QuestionCard from './QuestionCard';
4+
5+
interface ChatQuestionSectionProps {}
6+
7+
export const ChatQuestionSection = ({}: ChatQuestionSectionProps) => {
8+
const [expanded, setExpanded] = useState(false);
9+
10+
const toggleSection = () => {
11+
setExpanded((prev) => !prev);
12+
};
13+
14+
return (
15+
<SectionContainer>
16+
<QuestionCard
17+
type="host"
18+
user="부스트"
19+
message="설명해주셨던 내용 중에 yarn-berry를 설정했던 방법이 인상깊었는데 거기서 생겼던 오류가 있나요?"
20+
/>
21+
{expanded && <QuestionCard type="host" user="라이부" message="다른 질문들" />}
22+
<SwipeBtn onClick={toggleSection} />
23+
</SectionContainer>
24+
);
25+
};
26+
export default ChatQuestionSection;
27+
28+
const SectionContainer = styled.div`
29+
display: flex;
30+
flex-direction: column;
31+
justify-content: center;
32+
min-height: 95px;
33+
padding: 13px 20px 0px 20px;
34+
gap: 10px;
35+
border-top: 1px solid ${({ theme }) => theme.tokenColors['surface-alt']};
36+
border-bottom: 1px solid ${({ theme }) => theme.tokenColors['surface-alt']};
37+
overflow: hidden;
38+
`;
39+
40+
const SwipeBtn = styled.button`
41+
position: relative;
42+
width: 100%;
43+
height: 25px;
44+
cursor: pointer;
45+
46+
&::before {
47+
content: '';
48+
position: absolute;
49+
top: 35%;
50+
left: 50%;
51+
background-color: ${({ theme }) => theme.tokenColors['text-weak']};
52+
border-radius: 2px;
53+
height: 5px;
54+
width: 50px;
55+
transform: translate(-50%, -50%);
56+
}
57+
`;

frontend/src/components/chat/QuestionCard.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@ import styled from 'styled-components';
22
import CheckIcon from '@assets/icons/check.svg';
33

44
interface QuestionCardProps {
5-
type: 'normal' | 'question';
5+
type: 'host' | 'client';
6+
user: string;
7+
message: string;
68
}
79

8-
export const QuestionCard = ({ type }: QuestionCardProps) => {
10+
export const QuestionCard = ({ type, user, message }: QuestionCardProps) => {
911
return (
1012
<QuestionCardContainer>
1113
<QuestionCardTop>
1214
<QuestionInfo>
13-
<span className="name_info">💟 J999_부스트</span>
15+
<span className="name_info">💟 {user}</span>
1416
<span className="time_info">n분전</span>
1517
</QuestionInfo>
16-
{type === 'question' && (
18+
{type === 'host' && (
1719
<CheckBtn>
1820
<StyledCheckIcon />
1921
</CheckBtn>
2022
)}
2123
</QuestionCardTop>
2224

23-
<QuestionCardBottom>
24-
설명해주셨던 내용 중에 yarn-berry를 설정했던 방법이 인상깊었는데 거기서 생겼던 오류가 있나요?
25-
</QuestionCardBottom>
25+
<QuestionCardBottom>{message}</QuestionCardBottom>
2626
</QuestionCardContainer>
2727
);
2828
};

0 commit comments

Comments
 (0)