Skip to content

Commit 8a191af

Browse files
authored
Merge pull request #57 from SOPT-36-NINEDOT/feat/#54/jobDropdown
[Feat]: 회원가입 드롭다운 컴포넌트 제작
2 parents 5233eef + 247a436 commit 8a191af

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { style } from '@vanilla-extract/css';
2+
import { recipe } from '@vanilla-extract/recipes';
3+
4+
import { fonts, colors } from '@/style/token';
5+
6+
export const jobContainer = style({
7+
display: 'flex',
8+
justifyContent: 'space-between',
9+
alignItems: 'center',
10+
width: '52.2rem',
11+
height: '5rem',
12+
padding: '1.4rem 2rem',
13+
border: `1px solid ${colors.grey4}`,
14+
borderRadius: '8px',
15+
backgroundColor: colors.grey4,
16+
cursor: 'pointer',
17+
});
18+
19+
export const jobText = recipe({
20+
base: {
21+
...fonts.body03,
22+
textAlign: 'center',
23+
},
24+
variants: {
25+
state: {
26+
default: {
27+
color: colors.grey10,
28+
},
29+
clicked: {
30+
color: colors.grey6,
31+
},
32+
},
33+
},
34+
defaultVariants: {
35+
state: 'default',
36+
},
37+
});
38+
39+
export const dropdownIcon = recipe({
40+
base: {
41+
width: '2rem',
42+
height: '2rem',
43+
},
44+
variants: {
45+
state: {
46+
default: {
47+
color: colors.grey10,
48+
transform: 'rotate(0deg)',
49+
},
50+
clicked: {
51+
color: colors.grey6,
52+
transform: 'rotate(180deg)',
53+
},
54+
},
55+
},
56+
defaultVariants: {
57+
state: 'default',
58+
},
59+
});
60+
61+
export const textContainer = style({
62+
width: '52.2rem',
63+
height: '5rem',
64+
padding: '1.4rem 2rem',
65+
marginTop: '1.6rem',
66+
backgroundColor: colors.grey4,
67+
border: 'none',
68+
borderRadius: '8px',
69+
color: colors.grey10,
70+
...fonts.body03,
71+
72+
selectors: {
73+
'&::placeholder': {
74+
color: colors.grey6,
75+
},
76+
'&:focus': {
77+
outline: 'none',
78+
},
79+
},
80+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { useState } from 'react';
2+
3+
import { IcDropdown } from '@/assets/svg';
4+
import JobList from '@/common/component/JobDropDown/JobList';
5+
import {
6+
jobContainer,
7+
jobText,
8+
dropdownIcon,
9+
textContainer,
10+
} from '@/common/component/JobDropDown/JobDropDown.css';
11+
import { PLACE_HOLDER, JOB_LIST } from '@/common/component/JobDropDown/constants/job';
12+
import type { JobType, JobValue } from '@/common/component/JobDropDown/constants/job';
13+
14+
const JobDropDown = () => {
15+
const [isOpen, setIsOpen] = useState(false);
16+
const [selectedJob, setSelectedJob] = useState<JobValue>(PLACE_HOLDER);
17+
18+
const isPlaceHolder = typeof selectedJob === 'string';
19+
20+
const toggleDropdown = () => {
21+
setIsOpen((prev) => !prev);
22+
};
23+
24+
const handleJob = (job: JobType) => {
25+
setSelectedJob(job);
26+
setIsOpen(false);
27+
};
28+
29+
const state = isOpen ? 'clicked' : 'default';
30+
const isTextOpen = !isPlaceHolder && selectedJob.id === JOB_LIST[JOB_LIST.length - 1].id;
31+
32+
return (
33+
<>
34+
<button className={jobContainer} onClick={toggleDropdown}>
35+
<span className={jobText({ state })}>{isPlaceHolder ? selectedJob : selectedJob.job}</span>
36+
<IcDropdown className={dropdownIcon({ state })} />
37+
</button>
38+
39+
{isOpen && <JobList jobList={JOB_LIST} selectedJob={selectedJob} onSelect={handleJob} />}
40+
{isTextOpen && <input placeholder="정보를 입력해주세요" className={textContainer} />}
41+
</>
42+
);
43+
};
44+
45+
export default JobDropDown;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { style } from '@vanilla-extract/css';
2+
import { recipe } from '@vanilla-extract/recipes';
3+
4+
import { colors, fonts, zIndex } from '@/style/token';
5+
6+
export const listContainer = style({
7+
display: 'flex',
8+
flexDirection: 'column',
9+
width: '52.2rem',
10+
height: '24rem',
11+
backgroundColor: colors.grey2,
12+
borderRadius: '8px',
13+
overflowY: 'auto',
14+
overflowX: 'hidden',
15+
cursor: 'pointer',
16+
zIndex: zIndex.dropdown,
17+
});
18+
19+
export const listItem = style({
20+
display: 'flex',
21+
justifyContent: 'flex-start',
22+
alignItems: 'center',
23+
height: '5rem',
24+
padding: '1.4rem 2rem',
25+
borderBottom: `solid 1px ${colors.grey3}`,
26+
27+
selectors: {
28+
'&:last-child': {
29+
borderBottom: 'none',
30+
},
31+
'&:hover': {
32+
backgroundColor: colors.grey1,
33+
},
34+
},
35+
});
36+
37+
export const listText = recipe({
38+
variants: {
39+
state: {
40+
selected: {
41+
...fonts.body03,
42+
color: colors.grey10,
43+
},
44+
default: {
45+
...fonts.body01,
46+
color: colors.grey6,
47+
},
48+
},
49+
},
50+
defaultVariants: {
51+
state: 'default',
52+
},
53+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { listContainer, listItem, listText } from '@/common/component/JobDropDown/JobList.css';
2+
import type { JobType, JobValue } from '@/common/component/JobDropDown/constants/job';
3+
4+
type JobProps = {
5+
jobList: JobType[];
6+
selectedJob: JobValue;
7+
onSelect: (type: JobType) => void;
8+
};
9+
10+
const JobList = ({ jobList, selectedJob, onSelect }: JobProps) => {
11+
return (
12+
<div className={listContainer}>
13+
{jobList.map((job: JobType) => {
14+
const state = selectedJob === job ? 'selected' : 'default';
15+
16+
return (
17+
<button key={job.id} className={listItem} onClick={() => onSelect(job)}>
18+
<span className={listText({ state })}>{job.job}</span>
19+
</button>
20+
);
21+
})}
22+
</div>
23+
);
24+
};
25+
26+
export default JobList;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const JOB_LIST = [
2+
{ id: 1, job: '의사' },
3+
{ id: 2, job: '변호사' },
4+
{ id: 3, job: '개발자' },
5+
{ id: 4, job: '학생' },
6+
{ id: 5, job: '백수' },
7+
{ id: 6, job: '사업가' },
8+
{ id: 7, job: '운동선수' },
9+
{ id: 8, job: '기타 (직접 작성)' },
10+
];
11+
12+
export const PLACE_HOLDER = '직업을 선택하세요' as const;
13+
14+
export type JobType = {
15+
id: number;
16+
job: string;
17+
};
18+
export type JobValue = JobType | typeof PLACE_HOLDER;

0 commit comments

Comments
 (0)