Skip to content

Commit 83a9661

Browse files
authored
Merge pull request #69 from DevKor-github/feat/#68/post-layout
Feat/#68/post layout
2 parents 01a9765 + ee6f1f6 commit 83a9661

File tree

15 files changed

+250
-117
lines changed

15 files changed

+250
-117
lines changed

โ€Žsrc/common/components/Chip/index.tsxโ€Ž

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import type { PropsWithChildren } from 'react';
22
import * as s from './style.css';
33

44
interface Props extends PropsWithChildren {
5-
color?: 'gray' | 'main';
5+
isSelected?: boolean;
66
onClick?: () => void;
77
}
8-
const Chip = ({ children, color = 'gray', onClick }: Props) => {
8+
const Chip = ({ children, isSelected = false, onClick }: Props) => {
99
return (
10-
<div className={s.Container({ color })} onClick={onClick}>
10+
<div className={s.Container({ isSelected })} onClick={onClick}>
1111
{children}
1212
</div>
1313
);

โ€Žsrc/common/components/Chip/style.css.tsโ€Ž

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ export const Container = cva({
1515
border: '1px solid',
1616
},
1717
variants: {
18-
color: {
19-
main: {
18+
isSelected: {
19+
true: {
2020
backgroundColor: 'main-26',
2121
borderColor: 'main-54',
2222
color: '100',
2323
},
24-
gray: {
24+
false: {
2525
backgroundColor: 'systemGray5',
2626
borderColor: 'systemGray5',
2727
color: '80',

โ€Žsrc/common/components/TagOptionBtn/style.css.tsโ€Ž

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const Container = cva({
1010
lineHeight: 'normal',
1111
letterSpacing: '-0.04rem',
1212
border: '1px solid',
13+
transition: 'all 0.3s ease-in-out',
1314
},
1415
variants: {
1516
isSelected: {
@@ -58,14 +59,15 @@ export const leftIcon = css({
5859

5960
export const rightIcon = cva({
6061
base: {
61-
borderRadius: '0.76rem',
62-
width: '1.52225rem',
63-
height: '1.52225rem',
6462
display: 'flex',
63+
position: 'relative',
6564
justifyContent: 'center',
6665
alignItems: 'center',
67-
padding: '0.05rem 0.05rem 0 0',
68-
fontSize: '1.25rem',
66+
width: '1.52225rem',
67+
height: '1.52225rem',
68+
padding: '0.125rem',
69+
borderRadius: 'full',
70+
transition: 'all 0.3s ease-in-out',
6971
},
7072
variants: {
7173
isSelected: {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as s from './style.css';
2+
3+
interface Props {
4+
isSelected?: boolean;
5+
onClick?: () => void;
6+
}
7+
8+
const CheckBtn = ({ isSelected = false, onClick }: Props) => {
9+
const rightIconClass = isSelected ? 'mgc_check_fill' : null;
10+
11+
return (
12+
<div className={s.Container({ isSelected })} onClick={onClick}>
13+
<div className={`${rightIconClass}`} />
14+
</div>
15+
);
16+
};
17+
18+
export default CheckBtn;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { cva } from '@styled-system/css';
2+
3+
export const Container = cva({
4+
base: {
5+
display: 'flex',
6+
justifyContent: 'center',
7+
alignItems: 'center',
8+
width: '0.99019rem',
9+
height: '0.99019rem',
10+
borderRadius: 'full',
11+
color: '80',
12+
fontSize: '0.75rem',
13+
padding: '0.05rem 0.05rem 0 0',
14+
cursor: 'pointer',
15+
},
16+
variants: {
17+
isSelected: {
18+
true: {
19+
backgroundColor: 'main',
20+
},
21+
false: {
22+
backgroundColor: 'systemGray5',
23+
},
24+
},
25+
},
26+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useState } from 'react';
2+
import * as s from './style.css';
3+
4+
interface InputProps {
5+
isPrice?: boolean;
6+
}
7+
8+
const InputField = ({ isPrice = false }: InputProps) => {
9+
const [price, setPrice] = useState('');
10+
11+
const handleSetPrice = (event: React.ChangeEvent<HTMLInputElement>) => {
12+
let raw = event.target.value.replace(/,/g, '');
13+
14+
if (!/^\d*$/.test(raw)) return; // ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฌด์‹œ
15+
16+
const number = Number(raw);
17+
18+
if (isNaN(number)) {
19+
setPrice('');
20+
} else {
21+
setPrice(number.toLocaleString('ko-KR'));
22+
}
23+
24+
// TODO: ํ˜„์žฌ ์ž…๋ ฅ๊ฐ’์€ string
25+
// ๋ฐฑ์—”๋“œ์— ๋ณด๋‚ผ ๋•Œ number๋กœ ์žฌ๋ณ€ํ™˜ํ•˜๊ธฐ
26+
// const numericPrice = Number(price.replace(/,/g, ''));
27+
};
28+
29+
return <input
30+
className={s.Container({ isPrice })}
31+
value={isPrice ? price : undefined}
32+
onChange={isPrice ? handleSetPrice : undefined}
33+
inputMode={isPrice ? 'numeric' : 'text'}
34+
pattern={isPrice ? '[0-9]*' : undefined}
35+
></input>;
36+
};
37+
38+
export default InputField;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { cva } from '@styled-system/css';
2+
3+
export const Container = cva({
4+
base: {
5+
height: '2.75rem',
6+
backgroundColor: '#2C2C2E',
7+
width: '100%',
8+
padding: '0rem 1rem',
9+
borderRadius: '0.375rem',
10+
color: '100',
11+
opacity: '0.9',
12+
fontFamily: 'Pretendard',
13+
fontSize: '1rem',
14+
fontStyle: 'normal',
15+
fontWeight: '400',
16+
lineHeight: '1.4',
17+
},
18+
variants: {
19+
isPrice: {
20+
true: {
21+
textAlign: 'end',
22+
},
23+
false: {
24+
textAlign: 'start',
25+
},
26+
},
27+
},
28+
});

โ€Žsrc/features/post/components/StepFunnel/step2.tsxโ€Ž

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@ import Token from '@/common/components/Token';
22
import * as s from './style.css';
33

44
import TagOptionBtn from '@/common/components/TagOptionBtn';
5+
import { PRODUCT_TYPES_MAP, type ProductType } from '@/libs/types/post';
6+
import { useState } from 'react';
57

68
const Step2 = () => {
9+
const [selectedTypes, setSelectedTypes] = useState<ProductType[]>([]);
10+
11+
const handleSelectType = (type: ProductType) => {
12+
setSelectedTypes(prev => (prev.includes(type) ? prev.filter(t => t !== type) : [...prev, type]));
13+
};
14+
715
return (
816
<div>
917
<header className={s.Head}>
@@ -12,16 +20,14 @@ const Step2 = () => {
1220
</header>
1321
<div className={s.Content}>
1422
<div className={s.Grid}>
15-
<TagOptionBtn type="SOCCER" />
16-
<TagOptionBtn type="BASKETBALL" />
17-
<TagOptionBtn type="BASEBALL" />
18-
<TagOptionBtn type="HOCKEY" />
19-
<TagOptionBtn type="VARSITY_JACKET" />
20-
<TagOptionBtn type="SELF_MADE" />
21-
<TagOptionBtn type="ACCESSORY" />
22-
<TagOptionBtn type="VINTAGE" />
23-
<TagOptionBtn type="REFORM" />
24-
<TagOptionBtn type="OTHER" />
23+
{(Object.keys(PRODUCT_TYPES_MAP) as ProductType[]).map(type => (
24+
<TagOptionBtn
25+
key={type}
26+
type={type}
27+
isSelected={selectedTypes.includes(type)}
28+
onClick={() => handleSelectType(type)}
29+
/>
30+
))}
2531
</div>
2632
</div>
2733
</div>

โ€Žsrc/features/post/components/StepFunnel/step3.tsxโ€Ž

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@ import Chip from '@/common/components/Chip';
22
import * as s from './style.css';
33
import { TRADE_TYPES_MAP, type TradeType } from '@/libs/types/post';
44
import Token from '@/common/components/Token';
5+
import InputField from '../InputField';
6+
import { useState } from 'react';
57

68
const Step3 = () => {
9+
const [selectedTypes, setSelectedTypes] = useState<TradeType[]>([]);
10+
11+
const handleSelectType = (type: TradeType) => {
12+
setSelectedTypes(prev => (prev.includes(type) ? prev.filter(t => t !== type) : [...prev, type]));
13+
};
14+
715
return (
816
<div>
9-
<header className={s.Head}>
10-
๊ฑฐ๋ž˜ ๋ฐฉ์‹์„ ์„ ํƒํ•ด ์ฃผ์„ธ์š”
11-
</header>
17+
<header className={s.Head}>๊ฑฐ๋ž˜ ๋ฐฉ์‹์„ ์„ ํƒํ•ด ์ฃผ์„ธ์š”</header>
1218
<div className={s.Content}>
1319
<div className={s.DetailContent}>
1420
<div className={s.ContentHeader}>
@@ -19,25 +25,21 @@ const Step3 = () => {
1925
{(Object.entries(TRADE_TYPES_MAP) as [TradeType, string][])
2026
.filter(([key]) => key !== 'DIRECT_AND_PARCEL')
2127
.map(([key, label]) => (
22-
<Chip key={key}>{label}</Chip>
28+
<Chip key={key} isSelected={selectedTypes.includes(key)} onClick={() => handleSelectType(key)}>
29+
{label}
30+
</Chip>
2331
))}
2432
</div>
2533
</div>
2634
<div className={s.DetailContent}>
2735
<div className={s.HeaderInputField}>
2836
<span>์ง๊ฑฐ๋ž˜ ์žฅ์†Œ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”</span>
29-
<div style={{
30-
width: '100%',
31-
height: '2.75rem',
32-
backgroundColor: '#2C2C2E',
33-
borderRadius: '0.375rem'
34-
}} />
35-
{/* TODO: ์ž…๋ ฅ ํ•„๋“œ๋กœ ๊ณ ์น  ๊ฒƒ */}
37+
<InputField />
3638
</div>
3739
</div>
3840
</div>
39-
</div>);
40-
41+
</div>
42+
);
4143
};
4244

4345
export default Step3;
Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,53 @@
11
import Chip from '@/common/components/Chip';
22
import * as s from './style.css';
3-
import { COLOR_TYPES_MAP, QUALITY_TYPES_MAP, SIZE_TYPES_MAP } from '@/libs/types/post';
3+
import {
4+
COLOR_TYPES_MAP,
5+
QUALITY_TYPES_MAP,
6+
SIZE_TYPES_MAP,
7+
type ColorType,
8+
type QualityType,
9+
type SizeType,
10+
} from '@/libs/types/post';
11+
import { useState } from 'react';
12+
13+
const renderChipGroup = <T extends string>(
14+
map: Record<T, string>,
15+
selected: T | null,
16+
setSelected: React.Dispatch<React.SetStateAction<T | null>>,
17+
) => (
18+
<div className={s.ChipColumn}>
19+
{(Object.keys(map) as T[]).map(key => (
20+
<Chip key={key} isSelected={selected === key} onClick={() => setSelected(selected === key ? null : key)}>
21+
{map[key]}
22+
</Chip>
23+
))}
24+
</div>
25+
);
426

527
const Step4 = () => {
28+
const [selectedSizes, setSelectedSizes] = useState<SizeType | null>(null);
29+
const [selectedColors, setSelectedColors] = useState<ColorType | null>(null);
30+
const [selectedQualities, setSelectedQualities] = useState<QualityType | null>(null);
31+
632
return (
733
<div>
8-
<header className={s.Head}>
9-
ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•ด ์ฃผ์„ธ์š”
10-
</header>
34+
<header className={s.Head}>ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•ด ์ฃผ์„ธ์š”</header>
1135
<div className={s.Content}>
1236
<div className={s.DetailContent}>
1337
์‚ฌ์ด์ฆˆ๋ฅผ ์„ ํƒํ•ด ์ฃผ์„ธ์š”
14-
<div className={s.ChipColumn}>
15-
{Object.entries(SIZE_TYPES_MAP).map(([key, label]) => (
16-
<Chip key={key}>{label}</Chip>
17-
))}
18-
</div>
38+
{renderChipGroup(SIZE_TYPES_MAP, selectedSizes, setSelectedSizes)}
1939
</div>
2040
<div className={s.DetailContent}>
2141
์ƒ‰์ƒ์„ ์„ ํƒํ•ด ์ฃผ์„ธ์š”
22-
<div className={s.ChipColumn}>
23-
{Object.entries(COLOR_TYPES_MAP).map(([key, label]) => (
24-
<Chip key={key}>{label}</Chip>
25-
))}
26-
</div>
42+
{renderChipGroup(COLOR_TYPES_MAP, selectedColors, setSelectedColors)}
2743
</div>
2844
<div className={s.DetailContent}>
2945
ํ’ˆ์งˆ์„ ์„ ํƒํ•ด ์ฃผ์„ธ์š”
30-
<div className={s.ChipColumn}>
31-
{Object.entries(QUALITY_TYPES_MAP).map(([key, label]) => (
32-
<Chip key={key}>{label}</Chip>
33-
))}
34-
</div>
46+
{renderChipGroup(QUALITY_TYPES_MAP, selectedQualities, setSelectedQualities)}
3547
</div>
3648
</div>
37-
</div>);
38-
49+
</div>
50+
);
3951
};
4052

4153
export default Step4;

0 commit comments

Comments
ย (0)