Skip to content

Commit 6fa83a1

Browse files
authored
Merge pull request #23 from YAPP-Github/feat/#22
[FEAT] 공통 Chip 컴포넌트 구현
2 parents 4845247 + 50d8c62 commit 6fa83a1

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2+
3+
import Chip from './Chip';
4+
5+
const meta: Meta<typeof Chip> = {
6+
title: 'Shared/UI/Chip',
7+
component: Chip,
8+
parameters: {
9+
layout: 'centered',
10+
},
11+
tags: ['autodocs'],
12+
argTypes: {
13+
variant: {
14+
control: 'radio',
15+
options: ['line', 'fill'],
16+
description: '칩의 스타일 변형',
17+
},
18+
size: {
19+
control: 'radio',
20+
options: ['xs', 'sm', 'md'],
21+
description: '칩의 크기',
22+
},
23+
selected: {
24+
control: 'boolean',
25+
description: '선택 여부',
26+
},
27+
text: {
28+
control: 'text',
29+
description: '표시할 텍스트',
30+
},
31+
},
32+
};
33+
34+
export default meta;
35+
type Story = StoryObj<typeof Chip>;
36+
37+
export const Default: Story = {
38+
args: {
39+
text: 'Chip',
40+
variant: 'line',
41+
size: 'xs',
42+
selected: false,
43+
},
44+
};
45+
46+
export const LineSelected: Story = {
47+
args: {
48+
text: 'Selected Line',
49+
variant: 'line',
50+
size: 'sm',
51+
selected: true,
52+
},
53+
};
54+
55+
export const Fill: Story = {
56+
args: {
57+
text: 'Fill Mode',
58+
variant: 'fill',
59+
size: 'md',
60+
selected: false,
61+
},
62+
};
63+
64+
export const FillSelected: Story = {
65+
args: {
66+
text: 'Selected Fill',
67+
variant: 'fill',
68+
size: 'md',
69+
selected: true,
70+
},
71+
};
72+
73+
export const AllSizes: Story = {
74+
render: (args) => (
75+
<div className='flex flex-col gap-4'>
76+
<div className='flex items-center gap-2'>
77+
<span className='w-10 text-xs text-gray-500'>XS:</span>
78+
<Chip {...args} size='xs' />
79+
</div>
80+
<div className='flex items-center gap-2'>
81+
<span className='w-10 text-xs text-gray-500'>SM:</span>
82+
<Chip {...args} size='sm' />
83+
</div>
84+
<div className='flex items-center gap-2'>
85+
<span className='w-10 text-xs text-gray-500'>MD:</span>
86+
<Chip {...args} size='md' />
87+
</div>
88+
</div>
89+
),
90+
args: {
91+
text: 'Chip Size',
92+
variant: 'line',
93+
selected: false,
94+
},
95+
};

src/shared/ui/chip/Chip.tsx

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { ButtonHTMLAttributes } from 'react';
2+
3+
export type ChipVariant = 'line' | 'fill';
4+
export type ChipSize = 'xs' | 'sm' | 'md';
5+
6+
interface ChipProps extends ButtonHTMLAttributes<HTMLButtonElement> {
7+
/**
8+
* 칩 내부에 들어갈 텍스트입니다. children이 있으면 children이 우선됩니다.
9+
*/
10+
text?: string;
11+
/**
12+
* 칩의 스타일 변형입니다. 'line' 또는 'fill'을 선택할 수 있습니다.
13+
* @default 'line'
14+
*/
15+
variant?: ChipVariant;
16+
/**
17+
* 칩의 크기입니다. 'xs', 'sm', 'md' 중 선택할 수 있습니다.
18+
* @default 'xs'
19+
*/
20+
size?: ChipSize;
21+
/**
22+
* 칩의 선택 여부입니다. true일 경우 선택된 스타일이 적용됩니다.
23+
* @default false
24+
*/
25+
selected?: boolean;
26+
}
27+
28+
export default function Chip({
29+
children,
30+
text = 'chip',
31+
variant = 'line',
32+
size = 'xs',
33+
selected = false,
34+
className,
35+
...props
36+
}: ChipProps) {
37+
// 기본 스타일
38+
const baseStyles =
39+
'relative flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap rounded border transition-colors';
40+
41+
// 크기별 스타일 (Height, Padding, Typography)
42+
const sizeStyles = {
43+
xs: 'text-caption-7 h-6 px-2',
44+
sm: 'text-body-5 h-7 px-2.5',
45+
md: 'text-body-4 h-8 px-3',
46+
};
47+
48+
// 변형(Variant) 및 선택(Selected) 상태에 따른 색상 스타일
49+
let colorStyles = '';
50+
51+
if (variant === 'line') {
52+
if (selected) {
53+
colorStyles =
54+
'bg-blue-10 border-primary-default text-primary-default hover:bg-blue-30';
55+
} else {
56+
colorStyles =
57+
'bg-gray-0 border-line-nonclickable text-text-secondary hover:text-text-primary hover:bg-gray-50';
58+
}
59+
} else {
60+
// Fill
61+
if (selected) {
62+
// Hover brightness for 'main color' effect
63+
colorStyles =
64+
'bg-primary-default border-transparent text-text-inverse hover:brightness-95';
65+
} else {
66+
colorStyles =
67+
'bg-gray-100 border-transparent text-text-secondary hover:text-text-primary hover:bg-gray-200';
68+
}
69+
}
70+
71+
return (
72+
<button
73+
type='button'
74+
className={`${baseStyles} ${sizeStyles[size]} ${colorStyles} ${className || ''}`}
75+
{...props}
76+
>
77+
{children || text}
78+
</button>
79+
);
80+
}

src/shared/ui/chip/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './Chip';
2+
export * from './Chip';

0 commit comments

Comments
 (0)