From 58faa60de382209bd53c0102e902a17e5d6ca572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Tue, 8 Oct 2024 20:45:22 +0900 Subject: [PATCH 01/10] =?UTF-8?q?design(Header):=20border-bottom=201px=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=83=89=EC=83=81?= =?UTF-8?q?=20secondary=5F300=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Header/Header.style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Header/Header.style.ts b/frontend/src/components/Header/Header.style.ts index eebcf31fa..b36c8663f 100644 --- a/frontend/src/components/Header/Header.style.ts +++ b/frontend/src/components/Header/Header.style.ts @@ -17,7 +17,7 @@ export const HeaderContainer = styled.nav` padding: 0 2rem; background: white; - border-bottom: 2px solid ${theme.color.light.secondary_200}; + border-bottom: 1px solid ${theme.color.light.secondary_300}; @media (max-width: 768px) { padding: 0 1rem; From 1adf9acb84cf9aa4f327b33c570163e1b5791af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:16:39 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat(src):=20Textarea=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Textarea/Textarea.stories.tsx | 179 ++++++++++++++++++ .../src/components/Textarea/Textarea.style.ts | 101 ++++++++++ frontend/src/components/Textarea/Textarea.tsx | 118 ++++++++++++ frontend/src/components/index.ts | 1 + frontend/src/hooks/useInput.ts | 2 +- 5 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/Textarea/Textarea.stories.tsx create mode 100644 frontend/src/components/Textarea/Textarea.style.ts create mode 100644 frontend/src/components/Textarea/Textarea.tsx diff --git a/frontend/src/components/Textarea/Textarea.stories.tsx b/frontend/src/components/Textarea/Textarea.stories.tsx new file mode 100644 index 000000000..d662028d4 --- /dev/null +++ b/frontend/src/components/Textarea/Textarea.stories.tsx @@ -0,0 +1,179 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useState, ReactNode } from 'react'; + +import Textarea, { BaseProps, TextFieldProps } from './Textarea'; + +type TextareaStoryProps = BaseProps & { + children?: ReactNode; + minRows?: TextFieldProps['minRows']; + maxRows?: TextFieldProps['maxRows']; +}; + +const meta: Meta = { + title: 'Textarea', + component: Textarea, + argTypes: { + variant: { + control: { + type: 'radio', + options: ['filled', 'outlined', 'text'], + }, + }, + size: { + control: { + type: 'radio', + options: ['small', 'medium', 'large', 'xlarge'], + }, + }, + isValid: { + control: { + type: 'boolean', + }, + }, + minRows: { + control: { + type: 'number', + min: 1, + }, + }, + maxRows: { + control: { + type: 'number', + min: 1, + }, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Filled: Story = { + args: { + minRows: 3, + maxRows: 8, + }, + render: (args) => { + const [value, setValue] = useState(''); + + return ( +
+ +
+ ); + }, +}; + +export const Outlined: Story = { + args: { + variant: 'outlined', + minRows: 3, + maxRows: 8, + }, + render: (args) => { + const [value, setValue] = useState(''); + + return ( +
+ +
+ ); + }, +}; + +export const Text: Story = { + args: { + variant: 'text', + minRows: 3, + maxRows: 8, + }, + render: (args) => { + const [value, setValue] = useState(''); + + return ( +
+ +
+ ); + }, +}; + +export const WithLabel: Story = { + args: { + variant: 'outlined', + minRows: 3, + maxRows: 8, + }, + render: (args) => { + const [value, setValue] = useState(''); + + return ( +
+ +
+ ); + }, +}; + +export const WithHelperText: Story = { + args: { + variant: 'outlined', + size: 'medium', + isValid: false, + minRows: 3, + maxRows: 8, + }, + render: (args) => { + const [value, setValue] = useState(''); + + return ( +
+ +
+ ); + }, +}; diff --git a/frontend/src/components/Textarea/Textarea.style.ts b/frontend/src/components/Textarea/Textarea.style.ts new file mode 100644 index 000000000..aabfcdf3f --- /dev/null +++ b/frontend/src/components/Textarea/Textarea.style.ts @@ -0,0 +1,101 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; + +import { theme } from '@/style/theme'; + +import { BaseProps, TextFieldProps } from './Textarea'; + +const variants = (color: string | undefined) => ({ + filled: css` + background-color: ${color ?? '#ffffff'}; + `, + outlined: css` + background: none; + border: 1px solid ${color ?? theme.color.light.tertiary_400}; + `, + text: css` + background: none; + `, +}); + +const sizes = { + small: css` + padding: 8px; + font-size: 14px; + border-radius: 8px; + `, + medium: css` + padding: 12px; + font-size: 16px; + border-radius: 10px; + `, + large: css` + padding: 16px; + font-size: 18px; + border-radius: 12px; + `, + xlarge: css` + padding: 16px; + font-size: 32px; + border-radius: 12px; + `, +}; + +export const Container = styled.div` + cursor: text; + + display: flex; + flex-direction: column; + gap: 0.5rem; + + width: 100%; +`; + +export const Base = styled.div` + display: flex; + flex-direction: column; + width: 100%; + + ${({ variant, inputColor }) => variant && variants(inputColor)[variant]}; + ${({ size = 'medium' }) => sizes[size]}; + ${({ isValid }) => + isValid === false && + css` + border-color: ${theme.color.light.analogous_primary_400}; + `}; +`; + +export const TextareaField = styled.textarea` + resize: none; + + width: 100%; + + font-family: inherit; + font-size: inherit; + line-height: 1.5; + + background: none; + border: none; + outline: none; + + &::placeholder { + color: ${({ placeholderColor }) => placeholderColor || theme.color.light.tertiary_400}; + } + + &:disabled { + cursor: default; + opacity: 0.6; + } +`; + +export const Label = styled.label` + font-size: 1rem; + line-height: 100%; +`; + +export const HelperText = styled.span` + height: 1rem; + margin-left: 0.5rem; + font-size: 1rem; + color: ${theme.color.light.analogous_primary_400}; +`; diff --git a/frontend/src/components/Textarea/Textarea.tsx b/frontend/src/components/Textarea/Textarea.tsx new file mode 100644 index 000000000..6cf92a63c --- /dev/null +++ b/frontend/src/components/Textarea/Textarea.tsx @@ -0,0 +1,118 @@ +import { PropsWithChildren, useEffect, useRef } from 'react'; + +import { getChildOfType, getChildrenWithoutTypes } from '@/utils'; + +import * as S from './Textarea.style'; + +export interface BaseProps extends React.HTMLAttributes { + size?: 'small' | 'medium' | 'large' | 'xlarge'; + variant?: 'filled' | 'outlined' | 'text'; + isValid?: boolean; + inputColor?: string; +} + +export interface TextFieldProps extends React.TextareaHTMLAttributes { + size?: 'small' | 'medium' | 'large' | 'xlarge'; + minRows?: number; + maxRows?: number; + placeholderColor?: string; +} + +export interface LabelProps extends React.LabelHTMLAttributes {} + +export interface HelperTextProps extends React.HTMLAttributes {} + +const Label = ({ children, ...rests }: PropsWithChildren) => {children}; + +const HelperText = ({ children, ...rests }: PropsWithChildren) => ( + {children} +); + +const LabelType = (