From 7670d589f80c9976691bb690c17bb90d09f096ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=83=81=ED=98=84?= Date: Wed, 21 Dec 2022 16:48:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=84=B8=EA=B7=B8=EB=A9=98=ED=8A=B8=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=97=AC?= =?UTF-8?q?=EB=9F=AC=20=EB=B2=84=EA=B7=B8=EC=88=98=EC=A0=95=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: changeLog 추가 * chore: modify template * input 매개변수 및 마진 값 수정 (#12) * (#11) refactor: setState -> onChange로 변경 * (#11) fix: margin left 6px 추가 * (#14) feat: modal 매개변수로 children 추가 (#15) * fix: input label 옵셔널 * fix: modal children 옵셔널 * fix: search setState -> onChange * fix: index에서 name CheckBox로 export * (#19) feat: Segmented Button * fix: margin 로직 수정 * fix: input 에러메시지 빈 값일 때에도 추가 --- src/components/input/index.tsx | 4 +- src/components/search/index.tsx | 2 +- .../segmentedBtn/SegmentBtn.stories.tsx | 27 +++++++ src/components/segmentedBtn/index.tsx | 71 +++++++++++++++++++ src/components/styleGuide/text/Text.tsx | 2 +- src/index.ts | 3 +- src/utils/distance.ts | 47 ++++++++---- 7 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 src/components/segmentedBtn/SegmentBtn.stories.tsx create mode 100644 src/components/segmentedBtn/index.tsx diff --git a/src/components/input/index.tsx b/src/components/input/index.tsx index 225fd28..e9d0f16 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/index.tsx @@ -70,7 +70,9 @@ const _Input = styled.input<{ errorMsg: string }>` border-radius: 4px; border: 1px solid ${({ theme, errorMsg }) => - typeof errorMsg === 'undefined' ? theme.color.gray5 : theme.color.error}; + typeof errorMsg === 'undefined' || errorMsg === '' + ? theme.color.gray5 + : theme.color.error}; :focus { border: 2px solid ${({ theme }) => theme.color.primary}; } diff --git a/src/components/search/index.tsx b/src/components/search/index.tsx index 1ecca43..1cae95b 100644 --- a/src/components/search/index.tsx +++ b/src/components/search/index.tsx @@ -43,7 +43,7 @@ const _Wrapper = styled.label` align-items: center; width: 241px; height: 40px; - margin: ${({ margin }) => marginToCss({ margin })}; + ${({ margin }) => marginToCss({ margin })}; background: ${({ theme }) => theme.color.gray1}; box-shadow: 0 1px 20px rgba(204, 204, 204, 0.24); border-radius: 30px; diff --git a/src/components/segmentedBtn/SegmentBtn.stories.tsx b/src/components/segmentedBtn/SegmentBtn.stories.tsx new file mode 100644 index 0000000..df16d8e --- /dev/null +++ b/src/components/segmentedBtn/SegmentBtn.stories.tsx @@ -0,0 +1,27 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { useState } from 'react'; +import { SegmentedBtn } from '.'; + +export default { + title: 'component/segmentedBtn', + component: SegmentedBtn, +} as ComponentMeta; + +const selectedArr = ['A', 'B', 'C']; + +const Template: ComponentStory = (args) => { + const [state, setState] = useState(selectedArr[0]); + const onChange = (selected: string | number) => { + setState(selected); + }; + return ( + + ); +}; + +export const segmentedBtn = Template.bind({}); diff --git a/src/components/segmentedBtn/index.tsx b/src/components/segmentedBtn/index.tsx new file mode 100644 index 0000000..28a015c --- /dev/null +++ b/src/components/segmentedBtn/index.tsx @@ -0,0 +1,71 @@ +import styled, { css } from 'styled-components'; +import { marginCssType, marginToCss } from '../../utils/distance'; +import { Text } from '../styleGuide/text/Text'; + +interface PropsType extends marginCssType { + selectedArr: T[]; + cur: T; + onChange: (selected: T) => void; +} + +export const SegmentedBtn = ({ + selectedArr, + cur, + onChange, + margin, +}: PropsType) => { + return ( + <_Wrapper margin={margin}> + {selectedArr.map((selected) => ( + <_SelectedItem + onClick={() => onChange(selected)} + display="inline-block" + key={selected} + > + <_SelectedInner cur={selected === cur}>{selected} + + ))} + + ); +}; + +const _Wrapper = styled.div` + ${({ margin }) => marginToCss({ margin })}; + display: inline-block; + height: 48px; + border: 1px solid ${({ theme }) => theme.color.primaryLighten1}; + cursor: pointer; + border-radius: 4px; + > div { + :first-of-type { + left: -1px; + } + :last-of-type { + right: -1px; + } + } +`; + +const _SelectedItem = styled(Text)` + color: ${({ theme }) => theme.color.gray4}; + width: 110px; + height: 48px; + transition: 0.15s; + position: relative; + top: -1px; +`; + +const _SelectedInner = styled.div<{ cur: boolean }>` + display: flex; + justify-content: center; + align-items: center; + height: 100%; + transition: 0.15s; + ${({ theme, cur }) => + cur && + css` + background-color: ${theme.color.primary}; + border-radius: 4px; + color ${theme.color.gray1} + `}; +`; diff --git a/src/components/styleGuide/text/Text.tsx b/src/components/styleGuide/text/Text.tsx index f58f1b4..5cfb2bb 100644 --- a/src/components/styleGuide/text/Text.tsx +++ b/src/components/styleGuide/text/Text.tsx @@ -43,7 +43,7 @@ const _Text = styled.div` display: ${({ display }) => display}; color: ${({ color, theme }) => theme.color[color]}; ${({ size, theme }) => theme.font[size]}; - margin: ${({ margin }) => marginToCss({ margin })}; + ${({ margin }) => marginToCss({ margin })}; cursor: ${({ cursor }) => cursor}; text-align: ${({ align }) => align}; `; diff --git a/src/index.ts b/src/index.ts index 22aab68..7d5e32f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ export * from './components/button/Button'; -export * from './components/checkBox'; +export * as CheckBox from './components/checkBox'; export * from './components/dropdown'; export * from './components/input'; export * from './components/radio/Radio'; @@ -17,3 +17,4 @@ export * from './components/search'; export * from './components/styleGuide/icon'; export * from './components/modal'; export * from './components/breadCrumb/BreadCrumb'; +export * from './components/segmentedBtn'; diff --git a/src/utils/distance.ts b/src/utils/distance.ts index a32bf1d..21562a5 100644 --- a/src/utils/distance.ts +++ b/src/utils/distance.ts @@ -4,32 +4,53 @@ import styled from 'styled-components'; /** 이거 배열말고 그냥 매개변수로 받아야 타이핑이 줄어들음 */ type marginType = | [number, number, number, number] - | [number | directionType, number] + | [number | directionType, number | 'auto'] | [number]; export interface marginCssType { - margin?: marginType; + margin?: marginType[] | marginType; } -export const marginToCss = ({ margin }: marginCssType) => { - if (!margin) return; +const mgReturn = (mg: marginType) => { + if (mg[0] === 0 && mg[1] === 'auto') { + return `margin: 0 auto;`; + } - switch (margin[0]) { + const unitTransform = (m: 'auto' | number) => (m === 'auto' ? m : m + 'px'); + + switch (mg[0]) { case 'top': - return `${margin[1]}px 0 0 0`; + return `margin-top: ${unitTransform(mg[1])};`; case 'bottom': - return `0 0 ${margin[1]}px 0`; + return `margin-bottom: ${unitTransform(mg[1])};`; case 'left': - return `0 0 0 ${margin[1]}px`; + return `margin-left: ${unitTransform(mg[1])};`; case 'right': - return `0 ${margin[1]}px 0 0`; + return `margin-right: ${unitTransform(mg[1])};`; default: - let css = ''; - for (let i = 0; i < margin.length; i++) css += margin[i] + 'px '; - return css; + let css = 'margin: '; + for (let j = 0; j < mg.length; j++) css += mg[j] + 'px '; + return css + ';'; } }; +export const marginToCss = ({ margin }: marginCssType) => { + if (!margin) return; + let mgCss = ''; + + if (Array.isArray(margin[0])) { + for (let i = 0; i < margin.length; i++) { + // @ts-expect-error + mgCss += mgReturn(margin[i]); + } + } else { + // @ts-expect-error + mgCss = mgReturn(margin); + } + + return mgCss; +}; + export const _Wrapper = styled.div` - margin: ${({ margin }) => marginToCss({ margin })}; + ${({ margin }) => marginToCss({ margin })}; `;