From 059b7584fc50627bb8e5dd8d5d3b1059d9cea321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A7=80=ED=98=84?= <98106371+easyhyun00@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:02:25 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Chip=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat: Chip 컴포넌트 제작 * ✨ feat: Chip 스토리북 작성 * 📝 style: color 상수 적용 및 rem 단위로 변경 * 💄 design: bottle-tag 스타일 변경 * ✨ feat: VariantType에 bottle-tag-bubble 추가 * 📝 style: storybook 이름 변경 및 rem 단위로 변경 * 📝 style: ChipProps interface 로 변경 및 button 타입 설정 및 SerializedStyles 제거 --- src/components/Chip/index.stories.tsx | 148 ++++++++++++++++++++++++++ src/components/Chip/index.tsx | 25 +++++ src/components/Chip/styles.ts | 122 +++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 src/components/Chip/index.stories.tsx create mode 100644 src/components/Chip/index.tsx create mode 100644 src/components/Chip/styles.ts diff --git a/src/components/Chip/index.stories.tsx b/src/components/Chip/index.stories.tsx new file mode 100644 index 00000000..e1444f5f --- /dev/null +++ b/src/components/Chip/index.stories.tsx @@ -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; + +export default meta; + +type Story = StoryObj; + +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([]); + + 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 ( +
+

최대 3개까지만 선택 가능합니다.

+
+ {chipLabels.map((label) => ( + handleChipClick(label)} + css={styles.chip} + > + {label} + + ))} +
+
+ ); + }; + + return ; + }, +}; + +export const 물병_칩: StoryObj = { + render: () => ( +
+ + +

26h

+
+ NEW + 답장이 도착했어요 +
+ ), +}; + +export const 필터링_칩: StoryObj = { + render: () => ( +
+ 17~ + 모두에게 + 진로 +
+ ), +}; + +export const 폼_칩: StoryObj = { + render: () => ( +
+
+ + 모두에게 보내기 + + + 나와 같은 성별에게 보내기 + +
+
+ + 일·직장 + +
+
+ ), +}; diff --git a/src/components/Chip/index.tsx b/src/components/Chip/index.tsx new file mode 100644 index 00000000..2af60c3e --- /dev/null +++ b/src/components/Chip/index.tsx @@ -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 ( + + ); +}; + +export default Chip; diff --git a/src/components/Chip/styles.ts b/src/components/Chip/styles.ts new file mode 100644 index 00000000..eb2a589c --- /dev/null +++ b/src/components/Chip/styles.ts @@ -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;