Skip to content

Commit ca79b5e

Browse files
authored
Merge pull request #102 from DevKor-github/feat/#97/filter-control
[#97] 필터 만들기 1편
2 parents bab8d43 + 78f3b1f commit ca79b5e

21 files changed

Lines changed: 603 additions & 45 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"date-fns": "^4.1.0",
2121
"mingcute_icon": "^2.9.6",
2222
"motion": "^12.23.3",
23+
"qs": "^6.14.0",
2324
"react": "^19.1.0",
2425
"react-dom": "^19.1.0",
2526
"react-router": "^7.6.3",
@@ -32,6 +33,7 @@
3233
"@pandacss/dev": "^0.53.7",
3334
"@tanstack/eslint-plugin-query": "^5.78.0",
3435
"@types/node": "^24.0.13",
36+
"@types/qs": "^6.14.0",
3537
"@types/react": "^19.1.2",
3638
"@types/react-dom": "^19.1.2",
3739
"@vitejs/plugin-react": "^4.4.1",
@@ -56,4 +58,4 @@
5658
"eslint --fix"
5759
]
5860
}
59-
}
61+
}

src/common/components/Drawer/DrawerBody.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useEffect, useRef, useState, type PropsWithChildren } from 'react';
22
import { cx } from '@styled-system/css';
3-
import { motion, type PanInfo } from 'framer-motion';
3+
import { motion, type PanInfo } from 'motion/react';
44

55
import * as s from './style.css';
66

@@ -44,7 +44,7 @@ const DrawerBody = ({ children, title, description, close }: DrawerBodyProps) =>
4444
className={s.Container}
4545
drag={'y'}
4646
dragConstraints={{ top: 0, bottom: 0 }}
47-
transition={{ duration: 0.2 }}
47+
transition={{ duration: 0.35 }}
4848
dragElastic={0.4}
4949
variants={{
5050
opened: { top: `calc(100dvh - ${height}px)` },
@@ -55,15 +55,15 @@ const DrawerBody = ({ children, title, description, close }: DrawerBodyProps) =>
5555
exit={'closed'}
5656
onDragEnd={onDragEnd}
5757
>
58-
<header className={s.Header}>
59-
<div className={s.HeaderTitle}>
60-
<span className={s.Title}>{title}</span>
61-
{description && <span className={s.Description}>{description}</span>}
62-
</div>
63-
<button className={cx('mgc_close_line', s.CloseButton)} onClick={close} />
64-
</header>
65-
<div ref={contentRef} className={s.Content}>
66-
{children}
58+
<div ref={contentRef} className={s.Wrapper}>
59+
<header className={s.Header}>
60+
<div className={s.HeaderTitle}>
61+
<span className={s.Title}>{title}</span>
62+
{description && <span className={s.Description}>{description}</span>}
63+
</div>
64+
<button className={cx('mgc_close_line', s.CloseButton)} onClick={close} />
65+
</header>
66+
<div className={s.Content}>{children}</div>
6767
</div>
6868
</motion.div>
6969
);

src/common/components/Drawer/style.css.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const BackLayerStyle = css({
1111
});
1212

1313
export const Container = css({
14-
pos: 'fixed',
14+
pos: 'absolute',
1515
top: '100dvh',
1616
left: 0,
1717
w: 'full',
@@ -24,6 +24,10 @@ export const Container = css({
2424
flexDir: 'column',
2525
borderTop: '1px solid {colors.systemGray4}',
2626
boxShadow: '0px 0px 30px 0px rgba(0, 0, 0, 0.25)',
27+
});
28+
29+
export const Wrapper = css({
30+
w: 'full',
2731
pt: '1.5rem',
2832
pb: '2.625rem',
2933
});

src/common/components/SelectButton/style.css.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const Container = cva({
1414
color: '100',
1515
border: '1px solid',
1616
lineHeight: '0.87788rem',
17+
flexShrink: 0,
1718
},
1819
variants: {
1920
active: {

src/common/utils/client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import qs from 'qs';
12
import axios, { AxiosError } from 'axios';
23

34
const client = axios.create({
@@ -6,6 +7,8 @@ const client = axios.create({
67
'Content-Type': 'application/json',
78
},
89
withCredentials: true,
10+
// 같은 쿼리 파라미터를 배열로 감싸 중복해서 보내기위해서 qs 사용
11+
paramsSerializer: params => qs.stringify(params, { arrayFormat: 'repeat' }),
912
});
1013

1114
const REFRESH_URL = '/api/v1/refresh-token';
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// TODO: 디자인 확정나면 구현
2+
// 이거 근데 Pick 페이지랑 공유될 거 같음
3+
// 개발 진척도 보고 Common으로 빼든가 하기
4+
5+
import * as s from './style.css';
6+
7+
const DatePicker = () => {
8+
return (
9+
<div className={s.DatePickerContainer}>
10+
<h1>대충 달력</h1>
11+
<p>일년은삼백육십오일일년은삼백육십오일</p>
12+
<p>일년은삼백육십오일일년은삼백육십오일</p>
13+
<p>일년은삼백육십오일일년은삼백육십오일</p>
14+
<p>사실은삼백육십오일쩜이사이이일</p>
15+
</div>
16+
);
17+
};
18+
export default DatePicker;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { cx } from '@styled-system/css';
2+
import * as s from './style.css';
3+
import useDrawer from '@/common/hooks/useDrawer';
4+
import Drawer from '@/common/components/Drawer';
5+
import DatePicker from '@/features/home/components/DatePickButton/DatePicker';
6+
7+
const DatePickButton = () => {
8+
const { open, drawerState } = useDrawer();
9+
// TODO: 디자인 확정, 백엔드 API 나오면 디자인 및 로직 추가
10+
return (
11+
<>
12+
<button className={s.Container} onClick={open}>
13+
<span className={cx('mgc_calendar_fill', s.Icon)} />
14+
날짜선택
15+
</button>
16+
<Drawer title="대여 및 반납 일자" drawerState={drawerState}>
17+
<DatePicker />
18+
</Drawer>
19+
</>
20+
);
21+
};
22+
export default DatePickButton;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { css } from '@styled-system/css';
2+
3+
export const Container = css({
4+
display: 'flex',
5+
p: '0.25rem 0.625rem 0.25rem 0.5rem',
6+
alignItems: 'center',
7+
gap: '0.1875rem',
8+
rounded: '0.625rem',
9+
bg: 'systemGray5',
10+
color: '100',
11+
fontSize: '0.75rem',
12+
fontWeight: 500,
13+
lineHeight: '0.87788rem',
14+
});
15+
16+
export const Icon = css({
17+
fontSize: '1rem',
18+
});
19+
20+
export const DatePickerContainer = css({
21+
display: 'flex',
22+
flexDirection: 'column',
23+
gap: '0.5rem',
24+
alignItems: 'center',
25+
px: '1rem',
26+
pt: '1.875rem',
27+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useSearchParams } from 'react-router';
2+
3+
import * as s from './style.css';
4+
5+
import { FilterTypeToList, type FilterType } from '@/features/home/types';
6+
import { type TagType } from '@/libs/types/item';
7+
import TagOptionBtn from '@/common/components/TagOptionBtn';
8+
9+
interface Props {
10+
type: FilterType;
11+
}
12+
const FilterContents = ({ type }: Props) => {
13+
const [searchParams, setSearchParams] = useSearchParams();
14+
const selected = searchParams.getAll(type) as TagType[];
15+
16+
const handleTOBClick = (t: TagType, isSelected: boolean) => {
17+
if (isSelected) {
18+
searchParams.delete(type, t);
19+
} else {
20+
searchParams.append(type, t);
21+
}
22+
setSearchParams(searchParams);
23+
};
24+
25+
return (
26+
<div className={s.Container}>
27+
{type === 'price' ? (
28+
<div>아 만들기 개귀찮네</div>
29+
) : (
30+
<div key={type} className={s.TagOptionButtonWrapper}>
31+
{FilterTypeToList[type].map(t => {
32+
const isSelected = selected.includes(t);
33+
return (
34+
<TagOptionBtn key={t} type={t} isSelected={isSelected} onClick={() => handleTOBClick(t, isSelected)} />
35+
);
36+
})}
37+
</div>
38+
)}
39+
</div>
40+
);
41+
};
42+
43+
export default FilterContents;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { css } from '@styled-system/css';
2+
3+
export const Container = css({
4+
width: 'full',
5+
pt: '1.3125rem',
6+
height: 'full',
7+
});
8+
9+
export const TagOptionButtonWrapper = css({
10+
display: 'grid',
11+
gridTemplateColumns: 'repeat(2, 1fr)',
12+
gap: '0.625rem',
13+
});

0 commit comments

Comments
 (0)