Skip to content

Commit ac6742f

Browse files
authored
Merge pull request #38 from prgrms-web-devcourse-final-project/feat/input#10
[style]/input, Selectbox#10
2 parents 534e691 + e08cf73 commit ac6742f

File tree

9 files changed

+215
-25
lines changed

9 files changed

+215
-25
lines changed

next.config.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
12
import type { NextConfig } from 'next';
23

4+
35
const nextConfig: NextConfig = {
46
// TurboPack 설정
57
experimental: {
68
turbo: {
79
rules: {
8-
'*.svg': {
10+
'.svg': {
911
loaders: ['@svgr/webpack'],
10-
as: '*.js',
12+
as: '.js',
1113
},
1214
},
1315
},
@@ -20,11 +22,11 @@ const nextConfig: NextConfig = {
2022
config.module.rules.push(
2123
{
2224
...fileLoaderRule,
23-
test: /\.svg$/i,
25+
test: /.svg$/i,
2426
resourceQuery: /url/,
2527
},
2628
{
27-
test: /\.svg$/i,
29+
test: /.svg$/i,
2830
issuer: fileLoaderRule.issuer,
2931
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
3032
use: [
@@ -38,9 +40,9 @@ const nextConfig: NextConfig = {
3840
],
3941
}
4042
);
41-
fileLoaderRule.exclude = /\.svg$/i;
43+
fileLoaderRule.exclude = /.svg$/i;
4244
return config;
4345
},
4446
};
45-
47+
4648
export default nextConfig;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"build": "next build",
1111
"start": "next start",
1212
"lint": "eslint . --ext .ts,.tsx,.js,.jsx"
13-
},
13+
},
1414
"lint-staged": {
1515
"*.{js,jsx,ts,tsx}": [
1616
"prettier --write",

src/app/design-system/page.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import Button from '@/shared/components/button/Button';
44
import TextButton from '@/shared/components/button/TextButton';
5-
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
5+
import Input from '@/shared/components/InputBox/Input';
66
import { useState } from 'react';
77
import { customToast } from '@/shared/components/toast/CustomToastUtils';
8+
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
89
import ConfirmPop from '@/shared/components/modalPop/ConfirmPop';
10+
import ChatInput from '@/shared/components/InputBox/ChatInput';
11+
import SelectBox from '@/shared/components/InputBox/SelectBox';
912

1013
function Page() {
1114
const [isModalOpen, setModalOpen] = useState(false);
@@ -22,20 +25,18 @@ function Page() {
2225

2326
{/* Input */}
2427
<div className="flex flex-col gap-2 space-y-2">
25-
<h3 className="text-lg font-medium border-b pb-1">Input</h3>
26-
{/* 여기 컴포넌트 삽입 */}
27-
</div>
28-
29-
{/* checkbox */}
30-
<div className="space-y-2">
31-
<h3 className="text-lg font-medium border-b pb-1">checkbox</h3>
32-
{/* 여기 컴포넌트 삽입 */}
28+
<h3 className="text-xl font-medium border-b pb-1">Input</h3>
29+
<Input placeholder="내용을 입력해주세요." id="test" />
30+
<Input placeholder="내용을 입력해주세요." id="test" variant="search" />
31+
<Input placeholder="칵테일을 검색해 보세요" id="test" variant="comment" />
32+
<Input placeholder="내용을 입력해주세요." id="test" size="lg" />
33+
<ChatInput placeholder="내용을 입력해주세요" id="test" />
3334
</div>
3435

3536
{/* select */}
3637
<div className="space-y-2">
3738
<h3 className="text-lg font-medium border-b pb-1">select</h3>
38-
{/* 여기 컴포넌트 삽입 */}
39+
<SelectBox option={['', '논알콜', '약한 도수', '중간 도수']} title="도수" />
3940
</div>
4041
</div>
4142

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { keyDown } from '@/shared/utills/keyDown';
2+
import { Ref } from 'react';
3+
4+
interface Props {
5+
id: string;
6+
placeholder: string;
7+
className?: string;
8+
ref?: Ref<HTMLTextAreaElement | null>;
9+
}
10+
11+
function ChatInput({ id, placeholder, className, ref }: Props) {
12+
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
13+
const target = e.currentTarget;
14+
15+
if (target.value == '') {
16+
target.style.height = '';
17+
}
18+
target.style.height = `${target.scrollHeight}px`;
19+
};
20+
21+
return (
22+
<div>
23+
<label htmlFor={id} className="sr-only">
24+
입력창
25+
</label>
26+
<textarea
27+
id={id}
28+
name={id}
29+
ref={ref}
30+
onKeyDown={(e) => keyDown(e)}
31+
onInput={(e) => handleInput(e)}
32+
placeholder={placeholder}
33+
className={` px-4 py-1 rounded-lg h-13 bg-white text-primary leading-11 placeholder:text-gray-dark resize-none outline-none ${className}`}
34+
/>
35+
</div>
36+
);
37+
}
38+
export default ChatInput;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import tw from '@/shared/utills/tw';
2+
import { cva } from 'class-variance-authority';
3+
import { HTMLInputTypeAttribute, Ref } from 'react';
4+
import Search from '@/shared/assets/icons/search_32.svg';
5+
import Button from '../button/Button';
6+
// select나올떄 자연스러운 처리 화살표 로테이트 [x]
7+
// 인풋 타입받을 수 있게 수정 [x]
8+
// 인풋접근성 라벨이 중요함 라벨 을 div에 묶어서 하거나 label로 인풋감싸거나 div로 묶고 같은 선상에두게 [x]
9+
// div안에 라벨이랑감싸기 [x]
10+
// 텍스트 에어리어 버전도 만들기
11+
// 인풋 잘림 = 라인height 인풋 높이랑 맞춰두기 [x]
12+
13+
interface Props {
14+
placeholder: string;
15+
type?: HTMLInputTypeAttribute;
16+
ref?: Ref<HTMLInputElement | null>;
17+
size?: 'default' | 'lg';
18+
variant?: 'default' | 'search' | 'comment';
19+
className?: string;
20+
onChange?: () => void;
21+
id: string;
22+
}
23+
24+
export const InputClass = cva(
25+
`px-4 py-1 w-80 rounded-lg bg-white text-primary flex items-center gap-2 placeholder:text-gray-dark`,
26+
{
27+
variants: {
28+
size: {
29+
default: 'h-10',
30+
lg: 'h-13',
31+
},
32+
},
33+
defaultVariants: {
34+
size: 'default',
35+
},
36+
}
37+
);
38+
39+
function Input({
40+
placeholder,
41+
type,
42+
ref,
43+
size,
44+
variant = 'default',
45+
className,
46+
id,
47+
onChange,
48+
...rest
49+
}: Props) {
50+
return (
51+
<div className={tw(InputClass({ size, className }))}>
52+
<label htmlFor={id} className="flex-1">
53+
<input
54+
id={id}
55+
type={type}
56+
placeholder={placeholder}
57+
className={`outline-none w-full flex-1 leading-${size}`}
58+
ref={ref}
59+
onChange={onChange}
60+
{...rest}
61+
/>
62+
</label>
63+
{variant === 'search' ? (
64+
<button type="button">
65+
<Search aria-label="검색버튼" />
66+
</button>
67+
) : variant === 'comment' ? (
68+
<Button
69+
color="purple"
70+
type="submit"
71+
size="auto"
72+
className="w-10 h-6 flex-center text-xs px-1.5 py-[1px] rounded-sm shadow-md"
73+
>
74+
입력
75+
</Button>
76+
) : null}
77+
</div>
78+
);
79+
}
80+
export default Input;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Ref, useState } from 'react';
2+
import Down from '@/shared/assets/icons/selectDown_24.svg';
3+
4+
interface Props {
5+
ref?: Ref<HTMLButtonElement | null>;
6+
option: string[];
7+
title: string;
8+
}
9+
10+
function SelectBox({ ref, option, title }: Props) {
11+
const [isOpen, setIsOpen] = useState(false);
12+
const [select, setSelect] = useState('');
13+
14+
const handleChoose = (v: string) => {
15+
setIsOpen(!isOpen);
16+
if (!v) {
17+
setSelect(title);
18+
} else {
19+
setSelect(v);
20+
}
21+
};
22+
23+
return (
24+
<div className="flex flex-col gap-2 relative h-6 w-30">
25+
<button
26+
ref={ref}
27+
className="flex gap-2 cursor-pointer text-base"
28+
onClick={() => setIsOpen(!isOpen)}
29+
aria-expanded={isOpen}
30+
>
31+
{select ? select : title}
32+
{isOpen ? (
33+
<Down className="rotate-180 duration-300" />
34+
) : (
35+
<Down className="rotate-0 duration-300" />
36+
)}
37+
</button>
38+
39+
<ul
40+
className={`w-30 bg-white text-gray-dark p-2 rounded-xl duration-200 absolute transition-all
41+
${isOpen ? 'opacity-100 top-8' : 'opacity-0 pointer-events-none top-4'}`}
42+
role="listbox"
43+
>
44+
{option.map((v, i) => (
45+
<li
46+
key={v + i}
47+
role="option"
48+
className="cursor-pointer p-1 hover:bg-secondary aria-selected:bg-secondary"
49+
onClick={() => handleChoose(v)}
50+
aria-selected={v === select}
51+
>
52+
{v || title}
53+
</li>
54+
))}
55+
</ul>
56+
</div>
57+
);
58+
}
59+
export default SelectBox;

src/shared/components/button/Button.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@ import tw from '@/shared/utills/tw';
22
import { cva } from 'class-variance-authority';
33
import { ButtonHTMLAttributes, Ref } from 'react';
44

5-
// 버튼속성을 다 받을 수 있게
6-
// 클래스네임, 기본적인건 다 props로 받을 수 있게
7-
// 버튼 보여질 경우 arial hidden을 넣고 아이콘의경우 aria-label넣는다
8-
// 속성값에 disabled넣었을때 알어서 바뀔수 있게 할 수 있게 수정하기.
9-
// Ref가 잘 되는지 확인해보기
10-
115
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
12-
size?: 'default' | 'sm';
6+
size?: 'default' | 'sm' | 'auto';
137
color?: 'default' | 'purple';
148
ref?: Ref<HTMLButtonElement | null>;
159
disable?: boolean;
1610
type?: 'submit' | 'button';
1711
children?: React.ReactNode;
1812
className?: string;
13+
onClick?: () => void;
1914
}
2015

2116
export const ButtonClass = cva(
@@ -29,8 +24,9 @@ export const ButtonClass = cva(
2924
purple: 'bg-tertiary text-secondary',
3025
},
3126
size: {
32-
default: 'h-10, min-w-25',
27+
default: 'h-10 min-w-25',
3328
sm: 'h-8 min-w-20',
29+
auto: 'w-auto',
3430
},
3531
},
3632
defaultVariants: {
@@ -48,6 +44,7 @@ function Button({
4844
className,
4945
ref,
5046
disabled,
47+
onClick,
5148
...rest
5249
}: Props) {
5350
return (
@@ -56,6 +53,7 @@ function Button({
5653
type={type}
5754
ref={ref}
5855
disabled={disabled}
56+
onClick={onClick}
5957
{...rest}
6058
>
6159
{children}

src/shared/styles/_base.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22
button {
33
cursor: pointer;
44
}
5+
6+
select{
7+
appearance:none
8+
}
59
}

src/shared/utills/keyDown.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const keyDown = (e: React.KeyboardEvent) => {
2+
if (e.key === 'Enter' && !e.shiftKey) {
3+
e.preventDefault();
4+
if (e.nativeEvent.isComposing) {
5+
return;
6+
}
7+
}
8+
};

0 commit comments

Comments
 (0)