diff --git a/src/components/global/CustomButton/CustomButton.tsx b/src/components/global/CustomButton/CustomButton.tsx index 1efbc578..da3f531f 100644 --- a/src/components/global/CustomButton/CustomButton.tsx +++ b/src/components/global/CustomButton/CustomButton.tsx @@ -2,7 +2,6 @@ import React from 'react'; import styled, { css, keyframes } from 'styled-components'; import { colors } from '../../../style/color'; - interface CustomButtonProps { /** * 버튼 내부 텍스트 @@ -55,6 +54,7 @@ const Button = styled.button<{ transition: all 0.2s ease-in-out; cursor: pointer; width: ${props => props.$fullWidth ? '100%' : 'auto'}; + color: ${colors.white}; // 크기 설정 ${props => { @@ -62,19 +62,19 @@ const Button = styled.button<{ case 'small': return css` padding: 8px 16px; - font-size: 14px; + font-size: 10px; height: 32px; `; case 'large': return css` padding: 16px 32px; - font-size: 18px; + font-size: 12px; height: 48px; `; default: // medium return css` padding: 12px 24px; - font-size: 16px; + font-size: 12px; height: 40px; `; } @@ -114,14 +114,13 @@ const Button = styled.button<{ default: // primary return css` background-color: ${colors.blue200}; - color: white; border: none; - box-shadow: 0 2px 4px rgba(79, 70, 229, 0.1); + box-shadow: 0 2px 4px ${colors.blue100}; &:hover:not(:disabled) { - background-color:blue; + background-color: ${colors.blue200}; transform: translateY(-1px); - box-shadow: 0 4px 6px rgba(79, 70, 229, 0.2); + box-shadow: 0 4px 6px ${colors.blue100}; } &:active:not(:disabled) { @@ -138,7 +137,7 @@ const Button = styled.button<{ &:focus { outline: none; - box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.3); + box-shadow: 0 0 0 3px ${colors.blue100}; } `; diff --git a/src/components/global/CustomInput/CustomInput.stories.tsx b/src/components/global/CustomInput/CustomInput.stories.tsx index 02a84ee7..0f104af3 100644 --- a/src/components/global/CustomInput/CustomInput.stories.tsx +++ b/src/components/global/CustomInput/CustomInput.stories.tsx @@ -1,7 +1,7 @@ // CustomInput.stories.tsx import type { Meta, StoryObj } from '@storybook/react'; import { CustomInput } from './CustomInput'; - +import { CustomButton } from '../CustomButton/CustomButton'; const meta = { title: 'Components/CustomInput', component: CustomInput, @@ -113,4 +113,18 @@ export const Playground: Story = { error: '', hint: '', }, +}; + + +export const WithSuffix: Story = { + args: { + label: '제목', + placeholder: '값을 입력해주세요', + variant: 'underlined', + suffix: ( + + 확인 + + ), + }, }; \ No newline at end of file diff --git a/src/components/global/CustomInput/CustomInput.tsx b/src/components/global/CustomInput/CustomInput.tsx index da9f18c2..90a93832 100644 --- a/src/components/global/CustomInput/CustomInput.tsx +++ b/src/components/global/CustomInput/CustomInput.tsx @@ -1,7 +1,9 @@ -// CustomInput.tsx -import { InputHTMLAttributes } from 'react'; + +import React, { InputHTMLAttributes, ReactNode } from 'react'; import styled, { css, keyframes } from 'styled-components'; +import { typography } from '../../../style/typography'; import { colors } from '../../../style/color'; +import theme from '../../../style/theme'; interface CustomInputProps extends Omit, 'size'> { /** @@ -20,10 +22,18 @@ interface CustomInputProps extends Omit, ' * 너비를 100%로 설정 */ fullWidth?: boolean; - /** + /** * 입력 필드 스타일 변형 */ variant?: 'outlined' | 'underlined'; + /** + * 입력 필드 오른쪽에 추가할 요소 + */ + suffix?: ReactNode; + /** + * 버튼을 포함하는 경우, marginBottom 설정 + */ + hasButton?: boolean; } const fadeIn = keyframes` @@ -37,67 +47,105 @@ const fadeIn = keyframes` } `; -const Container = styled.div<{ $fullWidth: boolean }>` +const Container = styled.div<{ $fullWidth: boolean; $hasButton: boolean }>` display: flex; flex-direction: column; - width: ${props => props.$fullWidth ? '100%' : 'auto'}; + width: ${({ $fullWidth }) => ($fullWidth ? '100%' : theme.size.maxWidth || 'auto')}; + ${({ $hasButton }) => $hasButton && css` + margin-bottom: 10px; /* 버튼이 있을 때 margin-bottom을 10px 추가 */ + `} `; const Label = styled.label` font-weight: 500; - color: ${colors.gray300}; - margin-bottom: 4px; + color: #374151; + margin-bottom: 5px; + font-size: ${typography.body2}; `; -const StyledInput = styled.input<{ - $error: boolean; - $fullWidth: boolean; - $variant: string; -}>` - display: block; - width: ${props => props.$fullWidth ? '100%' : 'auto'}; - transition: all 0.2s ease-in-out; +const InputWrapper = styled.div<{ $variant: string; $error: boolean; $disabled: boolean }>` + display: flex; + align-items: center; + width: 100%; + background-color: ${(props) => (props.$disabled ? colors.gray100 : 'transparent')}; + ${({ $variant, $error, $disabled }) => + $variant === 'outlined' + ? css` + border-radius: 5px; + border: ${$disabled ? 'none' : `1px solid ${$error ? colors.red200 : colors.gray300}`}; + padding: 0px 10px; + height: 40px; + + &:focus-within { + border-color: ${$error ? colors.red200 : colors.blue200}; + box-shadow: 0 0 0 3px ${$error ? colors.red100 : colors.blue100}; + } + ` + : css` + border: none; + border-bottom: 1px solid ${$error ? colors.red200 : colors.gray300}; + border-radius: 0; + padding: 5px 10px; + height: 32px; + + &:focus-within { + border-bottom-color: ${$error ? colors.red200 : colors.blue200}; + } + `} +`; + +const StyledInput = styled.input<{ $error: boolean }>` + flex: 1; + border: none; outline: none; - background-color: ${colors.white}; + background-color: transparent; font-size: 14px; &::placeholder { - color: ${colors.gray200}; + color: #9ca3af; + font-size: ${typography.body6}; } &:disabled { - background-color: ${colors.gray100}; + color: ${colors.gray300}; cursor: not-allowed; + font-size: 12px; } +`; - ${({ $variant, $error }) => $variant === 'outlined' ? css` - border-radius: 20px; - border: 2px solid ${$error ? colors.red300 : colors.gray200}; - padding: 8px 16px; - height: 32px; - - &:focus { - border-color: ${$error ? colors.red300 : colors.blue200}; - box-shadow: 0 0 0 3px ${$error ? 'rgba(239, 68, 68, 0.1)' : 'rgba(79, 70, 229, 0.1)'}; - } - ` : css` - border: none; - border-bottom: 2px solid ${$error ? colors.red300 : colors.gray200}; - border-radius: 0; - padding: 4px 0; - height: 24px; - - &:focus { - border-bottom-color: ${$error ? colors.red300 : colors.blue200}; - } - `} +const SuffixContainer = styled.div<{ $variant: string; $error: boolean }>` + margin-left: 8px; + margin-bottom: 10px; + ${({ $variant, $error }) => $variant === 'outlined' + ? css` + border-radius: 20px; + border: 2px solid ${$error ? colors.red300 : colors.gray200}; + padding: 8px 16px; + height: 32px; + + &:focus { + border-color: ${$error ? colors.red300 : colors.blue200}; + box-shadow: 0 0 0 3px ${$error ? 'rgba(239, 68, 68, 0.1)' : 'rgba(79, 70, 229, 0.1)'}; + } + ` + : css` + border: none; + border-bottom: 2px solid ${$error ? colors.red300 : colors.gray200}; + border-radius: 0; + padding: 4px 0; + height: 24px; + + &:focus { + border-bottom-color: ${$error ? colors.red300 : colors.blue200}; + } + `} `; const Message = styled.p<{ $error?: boolean }>` - font-size: 14px; + font-size: ${typography.body6}; margin-top: 4px; - color: ${props => props.$error ? colors.red300 : colors.gray200}; - animation: ${fadeIn} 0.2s ease-in-out; + color: ${(props) => (props.$error ? colors.red300 : '#6b7280')}; + animation: ${fadeIn} 0.3s ease-in-out; /* 애니메이션 시간 증가 */ `; export function CustomInput({ @@ -107,23 +155,19 @@ export function CustomInput({ fullWidth = false, disabled = false, variant = 'outlined', + suffix, + hasButton = false, ...props }: CustomInputProps) { return ( - + {label && } - - {(error || hint) && ( - - {error || hint} - - )} + + + {suffix && {suffix}} + + {(error || hint) && {error || hint}} ); -} +}; +