diff --git a/packages/app/src/features/register/molecules/ProfileInputs/index.tsx b/packages/app/src/features/register/molecules/ProfileInputs/index.tsx index 33ad6a8f..b045d9cb 100644 --- a/packages/app/src/features/register/molecules/ProfileInputs/index.tsx +++ b/packages/app/src/features/register/molecules/ProfileInputs/index.tsx @@ -1,4 +1,4 @@ -import { Input, Profile, Select, SearchInput } from '@sms/shared' +import { Input, Profile, Select, SearchInput, Textarea } from '@sms/shared' import { InputColumn, FormWrapper } from '@features/register/atoms' import { RegisterFormType } from '@features/register/type' import { @@ -57,13 +57,12 @@ const ProfileInputs = ({ /> - resetField('introduce')} placeholder='1줄 자기소개 입력' /> diff --git a/packages/app/src/features/register/molecules/ProjectsInput/index.tsx b/packages/app/src/features/register/molecules/ProjectsInput/index.tsx index cab3f6d9..ff8647bd 100644 --- a/packages/app/src/features/register/molecules/ProjectsInput/index.tsx +++ b/packages/app/src/features/register/molecules/ProjectsInput/index.tsx @@ -5,7 +5,7 @@ import { FourImageInputs, IconImageInput, } from '@features/register/atoms' -import { Input, SearchInput, MultiMonthPicker } from '@sms/shared' +import { Input, SearchInput, MultiMonthPicker, Textarea } from '@sms/shared' import { RegisterFormType } from '@features/register/type' import * as Icon from '@sms/shared/src/icons' import { @@ -115,7 +115,7 @@ const ProjectsInput = ({ - - theme.body2} color: var(--N40); + white-space: pre-line; ` export const LinkWrapper = styled(Link)` diff --git a/packages/shared/package.json b/packages/shared/package.json index 1b11c9d4..726086cc 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -32,6 +32,7 @@ "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "next": "13.3.0", - "react-hook-form": "^7.43.9" + "react-hook-form": "^7.43.9", + "react-textarea-autosize": "^8.5.3" } } diff --git a/packages/shared/src/atoms/Textarea/index.stories.ts b/packages/shared/src/atoms/Textarea/index.stories.ts new file mode 100644 index 00000000..c8ccd90a --- /dev/null +++ b/packages/shared/src/atoms/Textarea/index.stories.ts @@ -0,0 +1,14 @@ +import { Meta, StoryObj } from '@storybook/react' +import Textarea from './index' + +const config: Meta = { + title: 'Textarea', + component: Textarea, + args: { error: 'ofweji', placeholder: 'hello' }, +} + +export default config + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/packages/shared/src/atoms/Textarea/index.tsx b/packages/shared/src/atoms/Textarea/index.tsx new file mode 100644 index 00000000..d542d009 --- /dev/null +++ b/packages/shared/src/atoms/Textarea/index.tsx @@ -0,0 +1,22 @@ +import { TextareaAutosizeProps } from 'react-textarea-autosize' +import { forwardRef } from 'react' +import * as S from './style' + +interface Props extends TextareaAutosizeProps { + error?: string +} + +const Textarea = forwardRef( + ({ error, ...props }, ref) => { + return ( + + + {error && {error}} + + ) + } +) + +Textarea.displayName = 'Textarea' + +export default Textarea diff --git a/packages/shared/src/atoms/Textarea/style.ts b/packages/shared/src/atoms/Textarea/style.ts new file mode 100644 index 00000000..cfc65778 --- /dev/null +++ b/packages/shared/src/atoms/Textarea/style.ts @@ -0,0 +1,36 @@ +import TextareaAutosize from 'react-textarea-autosize' +import styled from '@emotion/styled' + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; +` + +export const TextareaField = styled(TextareaAutosize)` + ${({ theme }) => theme.body1} + width: 100%; + border: 1px solid transparent; + border-radius: 0.5rem; + padding: 0.75rem; + resize: none; + outline: none; + background: var(--N10); + transition: 0.2s; + + ::placeholder { + color: var(--N30); + } + + :hover { + border: 1px solid var(--P1); + } + :focus { + border: 1px solid var(--P2); + } +` + +export const Error = styled.div` + ${({ theme }) => theme.caption1} + color: var(--ERROR); + margin-top: 0.5rem; +` diff --git a/packages/shared/src/atoms/index.ts b/packages/shared/src/atoms/index.ts index 33755cf5..bf44ac6b 100644 --- a/packages/shared/src/atoms/index.ts +++ b/packages/shared/src/atoms/index.ts @@ -17,3 +17,4 @@ export { default as Checkbox } from './Checkbox' export { default as Toast } from './Toast' export { default as ImageInput } from './ImageInput' export { default as MonthPicker } from './MonthPicker' +export { default as Textarea } from './Textarea' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdd59ec0..afe18bd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,6 +137,9 @@ importers: react-hook-form: specifier: ^7.43.9 version: 7.43.9(react@18.2.0) + react-textarea-autosize: + specifier: ^8.5.3 + version: 8.5.3(@types/react@18.0.33)(react@18.2.0) devDependencies: '@babel/preset-env': specifier: ^7.21.4 @@ -8475,6 +8478,20 @@ packages: engines: {node: '>=0.10.0'} dev: true + /react-textarea-autosize@8.5.3(@types/react@18.0.33)(react@18.2.0): + resolution: {integrity: sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.21.0 + react: 18.2.0 + use-composed-ref: 1.3.0(react@18.2.0) + use-latest: 1.2.1(@types/react@18.0.33)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -9661,6 +9678,41 @@ packages: punycode: 2.3.0 dev: true + /use-composed-ref@1.3.0(react@18.2.0): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.33)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.33 + react: 18.2.0 + dev: false + + /use-latest@1.2.1(@types/react@18.0.33)(react@18.2.0): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.33 + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.33)(react@18.2.0) + dev: false + /use-resize-observer@9.1.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==} peerDependencies: