Skip to content

Commit 17c821d

Browse files
committed
[style] 라디오, input, 쑤리 챗, 나의 챗 컴포넌트 작업
1 parent cd8ab8b commit 17c821d

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client';
2+
3+
import Send from '@/shared/assets/icons/send_36.svg';
4+
import { keyDown } from '@/shared/utills/keyDown';
5+
import { useState } from 'react';
6+
7+
function ChatForm() {
8+
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
9+
const target = e.currentTarget;
10+
11+
if (target.value == '') {
12+
target.style.height = '';
13+
}
14+
target.style.height = `${target.scrollHeight}px`;
15+
};
16+
17+
return (
18+
<form
19+
onSubmit={(e) => e.preventDefault()}
20+
className="absolute w-[calc(100%-24px)] bottom-12 left-3"
21+
>
22+
<div className="flex items-end w-full gap-2">
23+
<label htmlFor="chatInput" className="sr-only">
24+
질문 입력창
25+
</label>
26+
<textarea
27+
id="chatInput"
28+
name="chatInput"
29+
onKeyDown={(e) => keyDown(e)}
30+
onInput={(e) => handleInput(e)}
31+
placeholder="칵테일 추천 질문을 입력해주세요."
32+
className="w-[calc(100%-3.75rem)] px-4 py-3.5 rounded-lg h-[52px] max-h-[280px] bg-white text-primary placeholder:text-gray-dark resize-none outline-none"
33+
/>
34+
<button
35+
type="button"
36+
aria-label="보내기"
37+
className="flex-center w-13 h-13 rounded-xl border-1 border-white bg-secondary/20"
38+
>
39+
<Send className="text-secondary" />
40+
</button>
41+
</div>
42+
</form>
43+
);
44+
}
45+
export default ChatForm;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
interface Option {
2+
label: string;
3+
value: string;
4+
}
5+
6+
interface RadioGroupProps {
7+
options: Option[];
8+
value: string;
9+
onChange: (value: string) => void;
10+
}
11+
12+
function ChatRadio({ options, value, onChange }: RadioGroupProps) {
13+
return (
14+
<div role="radiogroup" className="flex flex-col gap-3 mt-5">
15+
{options.map((opt) => (
16+
<label
17+
htmlFor={`radio-${opt.value}`}
18+
key={opt.value}
19+
className="flex items-center gap-2 cursor-pointer"
20+
>
21+
<input
22+
id={`radio-${opt.value}`}
23+
type="radio"
24+
name="radio-group"
25+
value={opt.value}
26+
checked={value === opt.value}
27+
onChange={() => onChange(opt.value)}
28+
className="sr-only"
29+
/>
30+
<span
31+
className={`w-full rounded-3xl px-2 py-1 text-center transition-colors duration-150
32+
${value === opt.value ? 'bg-secondary shadow-[inset_0_0_6px_rgba(255,196,1,1)]' : 'bg-gray-dark/30'}
33+
hover:bg-secondary/100 `}
34+
>
35+
<span>{opt.label}</span>
36+
</span>
37+
</label>
38+
))}
39+
</div>
40+
);
41+
}
42+
export default ChatRadio;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
interface Message {
2+
id: string;
3+
sender: 'ssury' | 'user';
4+
text: string;
5+
type?: 'radio' | 'text';
6+
}
7+
8+
// 메시지 (연속 메시지)
9+
const messages: Message[] = [
10+
{
11+
id: '1',
12+
sender: 'user',
13+
text: '냥냥냥글자가길어지면 어케될까요 너무너무너무 궁금해요 하하하하하하하하하하하하하하하하하하ㅏ',
14+
},
15+
{ id: '2', sender: 'user', text: '배고파요' },
16+
];
17+
18+
function MyChat() {
19+
return (
20+
<article aria-label="내 메시지" className="flex flex-col items-end">
21+
<header className="w-fit">
22+
<strong></strong>
23+
</header>
24+
25+
{/* 메시지 그룹 */}
26+
<div className="flex flex-col items-end gap-3 mt-3 pr-3 max-w-[80%]">
27+
{messages.map((msg) => (
28+
<div
29+
key={msg.id}
30+
className="w-fit min-w-[120px] p-3 rounded-2xl rounded-tr-none bg-tertiary text-white"
31+
>
32+
<p className="whitespace-pre-line">{msg.text}</p>
33+
</div>
34+
))}
35+
</div>
36+
</article>
37+
);
38+
}
39+
export default MyChat;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use client';
2+
3+
import Ssury from '@/shared/assets/ssury/ssury_shaker.webp';
4+
import Image from 'next/image';
5+
import { useState } from 'react';
6+
import ChatRadio from './ChatRadio';
7+
import ChatCocktailCard from './ChatCocktailCard';
8+
9+
interface Message {
10+
id: string;
11+
text?: string;
12+
type?: 'radio' | 'text' | 'recommend';
13+
}
14+
15+
function SsuryChat() {
16+
const [selected, setSelected] = useState('option1');
17+
18+
// radio 옵션
19+
const options = [
20+
{ label: '옵션 1', value: 'option1' },
21+
{ label: '옵션 2', value: 'option2' },
22+
{ label: '옵션 3', value: 'option3' },
23+
];
24+
25+
// 메시지 (연속 메시지)
26+
const messages: Message[] = [
27+
{
28+
id: '1',
29+
text: '안녕하세요, 바텐더 쑤리에요. \n 취향에 맞는 칵테일을 추천해드릴게요!',
30+
},
31+
{
32+
id: '2',
33+
text: '어떤 유형으로 찾아드릴까요?',
34+
type: 'radio',
35+
},
36+
{
37+
id: '3',
38+
type: 'recommend',
39+
},
40+
];
41+
42+
return (
43+
<article aria-label="취향추천 챗봇 메시지" className="max-w-[80%]">
44+
<header className="flex items-end">
45+
<div className="relative w-20 h-20">
46+
<Image src={Ssury} alt="쑤리아바타" width={80} height={80} className="object-cover" />
47+
</div>
48+
<strong>쑤리</strong>
49+
</header>
50+
51+
{/* 메시지 그룹 */}
52+
<div className="flex flex-col gap-3 mt-3 pl-3">
53+
{messages.map((msg) => (
54+
<div key={msg.id}>
55+
{msg.type === 'recommend' ? (
56+
<ChatCocktailCard />
57+
) : (
58+
<div className="flex flex-col w-fit min-w-[120px] p-3 rounded-2xl rounded-tl-none bg-white text-black">
59+
{msg.text && <p className="whitespace-pre-line">{msg.text}</p>}
60+
61+
{/* radio */}
62+
{msg.type === 'radio' && (
63+
<ChatRadio options={options} value={selected} onChange={setSelected} />
64+
)}
65+
</div>
66+
)}
67+
</div>
68+
))}
69+
</div>
70+
</article>
71+
);
72+
}
73+
export default SsuryChat;

0 commit comments

Comments
 (0)