Skip to content

Commit

Permalink
Merge pull request #7 from peauty/feature/PEAUTY-36
Browse files Browse the repository at this point in the history
[PEAUTY-36] Input에 Button 추가 컴포넌트 생성
  • Loading branch information
myoungjinGo-FE authored Nov 19, 2024
2 parents 9f193b6 + e23e9e4 commit 14af0e9
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 66 deletions.
17 changes: 8 additions & 9 deletions src/components/global/CustomButton/CustomButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import React from 'react';
import styled, { css, keyframes } from 'styled-components';
import { colors } from '../../../style/color';

interface CustomButtonProps {
/**
* 버튼 내부 텍스트
Expand Down Expand Up @@ -55,26 +54,27 @@ const Button = styled.button<{
transition: all 0.2s ease-in-out;
cursor: pointer;
width: ${props => props.$fullWidth ? '100%' : 'auto'};
color: ${colors.white};
// 크기 설정
${props => {
switch (props.size) {
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;
`;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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};
}
`;

Expand Down
16 changes: 15 additions & 1 deletion src/components/global/CustomInput/CustomInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -113,4 +113,18 @@ export const Playground: Story = {
error: '',
hint: '',
},
};


export const WithSuffix: Story = {
args: {
label: '제목',
placeholder: '값을 입력해주세요',
variant: 'underlined',
suffix: (
<CustomButton size="small">
확인
</CustomButton>
),
},
};
156 changes: 100 additions & 56 deletions src/components/global/CustomInput/CustomInput.tsx
Original file line number Diff line number Diff line change
@@ -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<InputHTMLAttributes<HTMLInputElement>, 'size'> {
/**
Expand All @@ -20,10 +22,18 @@ interface CustomInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, '
* 너비를 100%로 설정
*/
fullWidth?: boolean;
/**
/**
* 입력 필드 스타일 변형
*/
variant?: 'outlined' | 'underlined';
/**
* 입력 필드 오른쪽에 추가할 요소
*/
suffix?: ReactNode;
/**
* 버튼을 포함하는 경우, marginBottom 설정
*/
hasButton?: boolean;
}

const fadeIn = keyframes`
Expand All @@ -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({
Expand All @@ -107,23 +155,19 @@ export function CustomInput({
fullWidth = false,
disabled = false,
variant = 'outlined',
suffix,
hasButton = false,
...props
}: CustomInputProps) {
return (
<Container $fullWidth={fullWidth}>
<Container $fullWidth={fullWidth} $hasButton={hasButton}>
{label && <Label>{label}</Label>}
<StyledInput
$error={!!error}
$fullWidth={fullWidth}
$variant={variant}
disabled={disabled}
{...props}
/>
{(error || hint) && (
<Message $error={!!error}>
{error || hint}
</Message>
)}
<InputWrapper $variant={variant} $error={!!error} $disabled={disabled}>
<StyledInput $error={!!error} disabled={disabled} {...props} />
{suffix && <SuffixContainer $variant={variant} $error={!!error}>{suffix}</SuffixContainer>}
</InputWrapper>
{(error || hint) && <Message $error={!!error}>{error || hint}</Message>}
</Container>
);
}
};

0 comments on commit 14af0e9

Please sign in to comment.