Skip to content

Commit 4c7e001

Browse files
committed
feat : 행사 & 장소 컨테이너
1 parent 5530673 commit 4c7e001

File tree

4 files changed

+325
-0
lines changed

4 files changed

+325
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Icon } from '@/shared/icons';
2+
import { cn } from '@/shared/lib';
3+
import { cva, type VariantProps } from 'class-variance-authority';
4+
5+
const addressCopyStyle = cva(
6+
'flex items-center justify-start flex-shrink-0 rounded-full transition-all duration-200',
7+
{
8+
variants: {
9+
variant: {
10+
gray: 'border border-gray-300 bg-gray-50 text-gray-400',
11+
mint: 'border border-mint-300 bg-mint-50 text-mint-500',
12+
},
13+
},
14+
defaultVariants: {
15+
variant: 'gray',
16+
},
17+
},
18+
);
19+
20+
interface AddressCopyProps
21+
extends React.HTMLAttributes<HTMLDivElement>,
22+
VariantProps<typeof addressCopyStyle> {
23+
label?: string;
24+
}
25+
26+
const AddressCopy = ({
27+
label = 'address copy',
28+
variant = 'gray',
29+
className,
30+
...props
31+
}: AddressCopyProps) => {
32+
return (
33+
<div
34+
className={cn(
35+
addressCopyStyle({ variant }),
36+
'w-[35.4rem] h-[4rem] px-[1.3rem] py-[1rem] gap-[0.4rem]',
37+
'cursor-pointer select-none',
38+
className,
39+
)}
40+
{...props}
41+
>
42+
<Icon
43+
name='CopySimple'
44+
size={18}
45+
color={variant === 'mint' ? 'mint-400' : 'gray-400'}
46+
/>
47+
<span
48+
className={cn(
49+
'text-label-lg',
50+
variant === 'mint' ? 'text-mint-400' : 'text-gray-400',
51+
)}
52+
>
53+
{label}
54+
</span>
55+
</div>
56+
);
57+
};
58+
59+
export default AddressCopy;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { cva, type VariantProps } from 'class-variance-authority';
2+
import { cn } from '@/shared/lib';
3+
4+
const cardStyle = cva(
5+
'flex flex-shrink-0 rounded-[1.6rem] transition-all duration-200',
6+
{
7+
variants: {
8+
variant: {
9+
gray: 'border border-gray-300 bg-gray-50',
10+
mint: 'border border-mint-300 bg-mint-50',
11+
},
12+
size: {
13+
medium:
14+
'w-[17rem] h-[17rem] flex-col items-center justify-center p-[1.2rem]',
15+
large:
16+
'w-[35.4rem] h-[12rem] flex-row items-start justify-start px-[1.3rem] pt-[1.1rem] pb-[3.3rem]',
17+
},
18+
},
19+
defaultVariants: {
20+
variant: 'gray',
21+
size: 'medium',
22+
},
23+
},
24+
);
25+
26+
interface CardProps
27+
extends React.HTMLAttributes<HTMLDivElement>,
28+
VariantProps<typeof cardStyle> {
29+
customHeight?: string;
30+
}
31+
32+
export const Card = ({
33+
className,
34+
variant,
35+
size,
36+
customHeight,
37+
...props
38+
}: CardProps) => {
39+
const customPadding =
40+
customHeight && size === 'large' ? 'px-[1.5rem] py-[1.4rem]' : '';
41+
return (
42+
<div
43+
className={cn(cardStyle({ variant, size }), customPadding, className)}
44+
style={customHeight ? { height: customHeight } : undefined}
45+
{...props}
46+
/>
47+
);
48+
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Icon } from '@/shared/icons';
2+
import { Card } from '@/shared/container/components/Card';
3+
import { cn } from '@/shared/lib';
4+
import Image from 'next/image';
5+
6+
interface EventCardProps {
7+
name: string;
8+
address: string;
9+
description: string;
10+
variant?: 'gray' | 'mint';
11+
size?: 'medium' | 'large';
12+
imageSrc?: string;
13+
}
14+
15+
const EventCard = ({
16+
name,
17+
address,
18+
description,
19+
variant = 'gray',
20+
size = 'medium',
21+
imageSrc = '',
22+
}: EventCardProps) => {
23+
return (
24+
<Card
25+
variant={variant}
26+
size={size}
27+
customHeight={size === 'large' ? '13rem' : undefined}
28+
>
29+
{/* Medium 카드 */}
30+
{size === 'medium' ? (
31+
<div className='flex flex-col justify-between w-full'>
32+
{/* 행사 사진 */}
33+
<div className="w-full h-[9rem] rounded-[2rem] mb-[1rem] overflow-hidden">
34+
<Image
35+
src={imageSrc}
36+
alt={name}
37+
width={142}
38+
height={90}
39+
className="object-cover w-full h-full"
40+
/>
41+
</div>
42+
{/* 행사 이름 + 하트 */}
43+
<div className='flex items-center justify-between w-full mb-[0.2rem]'>
44+
<span
45+
className={cn(
46+
'text-label-lg',
47+
variant === 'mint' ? 'text-mint-800' : 'text-gray-900',
48+
)}
49+
>
50+
{name}
51+
</span>
52+
<Icon
53+
name='HeartStraight'
54+
size={20}
55+
color={variant === 'mint' ? 'mint-400' : 'gray-300'}
56+
/>
57+
</div>
58+
{/* 행사 주소 */}
59+
<div
60+
className={cn(
61+
'text-body-md',
62+
variant === 'mint' ? 'text-mint-500' : 'text-gray-500',
63+
)}
64+
>
65+
{address}
66+
</div>
67+
</div>
68+
) : (
69+
// Large 카드
70+
<div className='flex items-start justify-between w-full gap-[1.2rem]'>
71+
{/* 행사 이미지 */}
72+
<div className="relative w-[14.2rem] h-[10rem] rounded-[2rem] flex-shrink-0 overflow-hidden">
73+
<Image
74+
src={imageSrc}
75+
alt={name}
76+
fill
77+
className="object-cover w-full h-full"
78+
/>
79+
</div>
80+
81+
82+
{/* 행사 제목 + 하트 */}
83+
<div className='flex flex-col justify-between flex-1'>
84+
<div className='flex items-start justify-between w-full'>
85+
<span
86+
className={cn(
87+
'text-label-lg',
88+
variant === 'mint' ? 'text-mint-800' : 'text-gray-900',
89+
)}
90+
>
91+
{name}
92+
</span>
93+
<Icon
94+
name='HeartStraight'
95+
size={20}
96+
color={variant === 'mint' ? 'mint-400' : 'gray-300'}
97+
/>
98+
</div>
99+
100+
{/* 행사 설명 */}
101+
<p
102+
className={cn(
103+
'text-body-md mt-[1rem]',
104+
variant === 'mint' ? 'text-mint-500' : 'text-gray-500',
105+
)}
106+
>
107+
{description}
108+
</p>
109+
</div>
110+
</div>
111+
)}
112+
</Card>
113+
);
114+
};
115+
116+
export default EventCard;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Icon } from '@/shared/icons';
2+
import { Card } from '@/shared/container/components/Card';
3+
import { cn } from '@/shared/lib';
4+
import Image from 'next/image';
5+
6+
interface LocationCardProps {
7+
name: string;
8+
address: string;
9+
description: string;
10+
variant?: 'gray' | 'mint';
11+
size?: 'medium' | 'large';
12+
imageSrc?: string;
13+
}
14+
15+
const LocationCard = ({
16+
name,
17+
address,
18+
description,
19+
variant = 'gray',
20+
size = 'medium',
21+
imageSrc = '',
22+
}: LocationCardProps) => {
23+
return (
24+
<Card variant={variant} size={size}>
25+
{/* Medium 카드 */}
26+
{size === 'medium' ? (
27+
<div className='flex flex-col justify-between w-full'>
28+
{/* 장소 사진 */}
29+
<div className="w-full h-[9rem] rounded-[2rem] mb-[1rem] overflow-hidden">
30+
<Image
31+
src={imageSrc}
32+
alt={name}
33+
width={145}
34+
height={90}
35+
className="object-cover w-full h-full"
36+
/>
37+
</div>
38+
39+
{/* 장소 이름 */}
40+
<div
41+
className={cn(
42+
'text-label-lg mb-[0.2rem]',
43+
variant === 'mint' ? 'text-mint-800' : 'text-gray-900',
44+
)}
45+
>
46+
{name}
47+
</div>
48+
{/* 장소 주소 */}
49+
<div
50+
className={cn(
51+
'text-body-md',
52+
variant === 'mint' ? 'text-mint-500' : 'text-gray-500',
53+
)}
54+
>
55+
{address}
56+
</div>
57+
</div>
58+
) : (
59+
// Large 카드
60+
<div className='flex flex-col justify-start w-full'>
61+
<div className='flex items-center justify-between w-full'>
62+
{/* 지도핀 + 장소 이름 + 하트 */}
63+
<div className='flex items-center gap-[0.4rem]'>
64+
<Icon
65+
name='MapPin'
66+
size={18}
67+
color={variant === 'mint' ? 'mint-500' : 'gray-600'}
68+
/>
69+
<span
70+
className={cn(
71+
'text-label-lg',
72+
variant === 'mint' ? 'text-mint-800' : 'text-gray-900',
73+
)}
74+
>
75+
{name}
76+
</span>
77+
</div>
78+
79+
<div className='flex-shrink-0 ml-auto'>
80+
<Icon
81+
name='HeartStraight'
82+
size={20}
83+
color={variant === 'mint' ? 'mint-400' : 'gray-300'}
84+
/>
85+
</div>
86+
</div>
87+
{/* 장소 설명 */}
88+
<p
89+
className={cn(
90+
'text-body-md mt-[0.5rem]',
91+
variant === 'mint' ? 'text-mint-500' : 'text-gray-500',
92+
)}
93+
>
94+
{description}
95+
</p>
96+
</div>
97+
)}
98+
</Card>
99+
);
100+
};
101+
102+
export default LocationCard;

0 commit comments

Comments
 (0)