-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ✨ feat: Chip 컴포넌트 제작 * ✨ feat: Chip 스토리북 작성 * 📝 style: color 상수 적용 및 rem 단위로 변경 * 💄 design: bottle-tag 스타일 변경 * ✨ feat: VariantType에 bottle-tag-bubble 추가 * 📝 style: storybook 이름 변경 및 rem 단위로 변경 * 📝 style: ChipProps interface 로 변경 및 button 타입 설정 및 SerializedStyles 제거
- Loading branch information
1 parent
3268968
commit 059b758
Showing
3 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { useState } from 'react'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { css } from '@emotion/react'; | ||
import { HourGlass } from '@/assets/icons'; | ||
import Chip from '.'; | ||
|
||
const meta = { | ||
title: 'Components/Chip', | ||
component: Chip, | ||
tags: ['autodocs'], | ||
} satisfies Meta<typeof Chip>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Primary: Story = { | ||
args: { | ||
children: '안녕하세요', | ||
}, | ||
}; | ||
|
||
const styles = { | ||
container: css` | ||
width: 100%; | ||
max-width: 37.5rem; | ||
background-color: skyblue; | ||
`, | ||
chipContainer: css` | ||
display: flex; | ||
flex-wrap: wrap; | ||
gap: 0.4375rem; | ||
justify-content: center; | ||
`, | ||
chip: css` | ||
margin-block: 0.4375rem; | ||
`, | ||
formContainer: css` | ||
display: flex; | ||
gap: 0.5rem; | ||
`, | ||
formChip: css` | ||
flex: 1; | ||
`, | ||
}; | ||
|
||
export const 고민_종류_칩: StoryObj = { | ||
render: () => { | ||
const ChipComponent = () => { | ||
type VariantType = 'primary' | 'primary-selected' | 'primary-disabled'; | ||
const [selectedChips, setSelectedChips] = useState<string[]>([]); | ||
|
||
const handleChipClick = (chip: string) => { | ||
setSelectedChips((currentChips) => { | ||
if (currentChips.includes(chip)) { | ||
return currentChips.filter((c) => c !== chip); | ||
} else if (currentChips.length < 3) { | ||
return [...currentChips, chip]; | ||
} | ||
return currentChips; | ||
}); | ||
}; | ||
|
||
const getVariant = (label: string): VariantType => { | ||
if (selectedChips.includes(label)) { | ||
return 'primary-selected'; | ||
} else if (selectedChips.length >= 3) { | ||
return 'primary-disabled'; | ||
} | ||
return 'primary'; | ||
}; | ||
|
||
const chipLabels = [ | ||
'일·직장', | ||
'취업·진로', | ||
'인간관계', | ||
'이별·상실', | ||
'연애', | ||
'학업', | ||
'가족', | ||
'기타', | ||
]; | ||
|
||
return ( | ||
<div css={styles.container}> | ||
<p>최대 3개까지만 선택 가능합니다.</p> | ||
<div css={styles.chipContainer}> | ||
{chipLabels.map((label) => ( | ||
<Chip | ||
key={label} | ||
variant={getVariant(label)} | ||
onClick={() => handleChipClick(label)} | ||
css={styles.chip} | ||
> | ||
{label} | ||
</Chip> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
return <ChipComponent />; | ||
}, | ||
}; | ||
|
||
export const 물병_칩: StoryObj = { | ||
render: () => ( | ||
<div css={styles.chipContainer}> | ||
<Chip variant="bottle-tag"> | ||
<HourGlass color="#828282" width={14} height={14} /> | ||
<p>26h</p> | ||
</Chip> | ||
<Chip variant="bottle-tag">NEW</Chip> | ||
<Chip variant="bottle-tag-bubble">답장이 도착했어요</Chip> | ||
</div> | ||
), | ||
}; | ||
|
||
export const 필터링_칩: StoryObj = { | ||
render: () => ( | ||
<div css={styles.chipContainer}> | ||
<Chip variant="filter">17~</Chip> | ||
<Chip variant="filter">모두에게</Chip> | ||
<Chip variant="filter">진로</Chip> | ||
</div> | ||
), | ||
}; | ||
|
||
export const 폼_칩: StoryObj = { | ||
render: () => ( | ||
<div css={{ backgroundColor: 'white', padding: '0.625rem' }}> | ||
<div css={styles.formContainer}> | ||
<Chip variant="form-selected" css={styles.formChip}> | ||
모두에게 보내기 | ||
</Chip> | ||
<Chip variant="form" css={styles.formChip}> | ||
나와 같은 성별에게 보내기 | ||
</Chip> | ||
</div> | ||
<div> | ||
<Chip variant="form" css={styles.formChip}> | ||
일·직장 | ||
</Chip> | ||
</div> | ||
</div> | ||
), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import style, { VariantType } from './styles'; | ||
|
||
interface ChipProps { | ||
/** Chip의 내용입니다. */ | ||
variant?: VariantType; | ||
/** Chip의 테마입니다. */ | ||
children: React.ReactNode; | ||
/** Chip의 클릭 이벤트 입니다. */ | ||
onClick?: () => void; | ||
} | ||
|
||
const Chip = ({ variant = 'primary', children, ...props }: ChipProps) => { | ||
return ( | ||
<button | ||
{...props} | ||
type="button" | ||
disabled={variant === 'primary-disabled'} | ||
css={style.chip(variant)} | ||
> | ||
{children} | ||
</button> | ||
); | ||
}; | ||
|
||
export default Chip; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { css } from '@emotion/react'; | ||
import COLORS from '@/constants/colors'; | ||
|
||
export type VariantType = | ||
| 'primary' | ||
| 'primary-selected' | ||
| 'primary-disabled' | ||
| 'bottle-tag' | ||
| 'bottle-tag-bubble' | ||
| 'filter' | ||
| 'form' | ||
| 'form-selected'; | ||
|
||
const styles = { | ||
chip: (variant: VariantType) => css` | ||
cursor: ${variant === 'primary-disabled' ? 'not-allowed' : 'pointer'}; | ||
${variants[variant]} | ||
`, | ||
}; | ||
|
||
const baseChipStyle = css` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
border-radius: 6.25rem; | ||
font-style: normal; | ||
text-align: center; | ||
backdrop-filter: blur(0.4688rem); | ||
`; | ||
|
||
const baseTextStyles = css` | ||
font-weight: 500; | ||
font-size: 1rem; | ||
line-height: 160%; | ||
`; | ||
|
||
const formChipStyles = css` | ||
${baseChipStyle}; | ||
padding: 0.75rem 1rem; | ||
font-weight: 700; | ||
line-height: 1rem; | ||
font-size: 0.75rem; | ||
color: ${COLORS.gray1}; | ||
`; | ||
|
||
const bottleTagStyles = css` | ||
${baseChipStyle}; | ||
background: white; | ||
font-weight: 500; | ||
gap: 0.25rem; | ||
padding: 0.25rem 0.75rem; | ||
font-size: 0.875rem; | ||
line-height: 1rem; | ||
border: 1px solid rgb(255 255 255 / 0.3); | ||
`; | ||
|
||
const variants = { | ||
primary: css` | ||
${baseChipStyle}; | ||
background: radial-gradient( | ||
491.85% 132.88% at 0% 12.5%, | ||
rgb(255 255 255 / 0.7) 0%, | ||
rgb(255 255 255 / 0.28) 100% | ||
); | ||
padding: 0.25rem 1rem; | ||
border: 1px solid rgb(255 255 255 / 0.3); | ||
color: var(--kakao-logo, #000); | ||
${baseTextStyles}; | ||
`, | ||
'primary-selected': css` | ||
${baseChipStyle}; | ||
${baseTextStyles}; | ||
padding: 0.25rem 1rem; | ||
border: 1px solid rgb(255 255 255 / 0.3); | ||
color: var(--kakao-logo, #000); | ||
`, | ||
'primary-disabled': css` | ||
${baseChipStyle}; | ||
${baseTextStyles}; | ||
padding: 0.25rem 1rem; | ||
border: 1px solid rgb(255 255 255 / 0.3); | ||
color: ${COLORS.gray4}; | ||
`, | ||
'bottle-tag': css` | ||
${bottleTagStyles}; | ||
`, | ||
'bottle-tag-bubble': css` | ||
${bottleTagStyles} | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
top: 1.5625rem; | ||
right: 1.375rem; | ||
border-width: 0.5rem; | ||
border-style: solid; | ||
border-color: white transparent transparent; | ||
} | ||
`, | ||
filter: css` | ||
${baseChipStyle}; | ||
padding: 0.25rem 0.5rem; | ||
font-weight: 500; | ||
gap: 0.625rem; | ||
border: 1px solid ${COLORS.gray4}; | ||
background: ${COLORS.gray5}; | ||
color: ${COLORS.gray1}; | ||
line-height: 1rem; | ||
font-size: 0.875rem; | ||
`, | ||
form: css` | ||
${formChipStyles}; | ||
background: white; | ||
border: 1px solid ${COLORS.gray5}; | ||
`, | ||
'form-selected': css` | ||
${formChipStyles}; | ||
border: 1px solid #2f80ed; | ||
background: rgb(47 128 237 / 0.2); | ||
`, | ||
}; | ||
|
||
export default styles; |