diff --git a/public/images/visibility-off.svg b/public/images/visibility-off.svg new file mode 100644 index 0000000..febddd1 --- /dev/null +++ b/public/images/visibility-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/visibility-on.svg b/public/images/visibility-on.svg new file mode 100644 index 0000000..a4c5284 --- /dev/null +++ b/public/images/visibility-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx new file mode 100644 index 0000000..2184c26 --- /dev/null +++ b/src/app/auth/page.tsx @@ -0,0 +1,66 @@ +'use client' + +import Input from '@components/Input' +import { useForm } from 'react-hook-form' + +interface FormData { + email: string + password: string +} + +export default function MyForm() { + const { + register, + handleSubmit, + formState: { errors, touchedFields }, + } = useForm({ + mode: 'onChange', + }) + + const onSubmit = (data: FormData) => { + console.log(data) + } + + return ( + + {/* 이메일 */} + + + {/* 비밀번호 */} + + + + 제출하기 + + + ) +} diff --git a/src/app/globals.css b/src/app/globals.css index 203f024..53f7894 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -31,6 +31,12 @@ body { .Text-white { @apply text-[#FFFFFF] dark:text-[#333236]; } +.Text-error { + @apply text-[#D6173A]; +} +.Border-error { + @apply border border-[#D6173A]; +} .Border-btn { @apply border border-[#D9D9D9] dark:border-[#747474]; } diff --git a/src/app/shared/components/Input.tsx b/src/app/shared/components/Input.tsx new file mode 100644 index 0000000..137f448 --- /dev/null +++ b/src/app/shared/components/Input.tsx @@ -0,0 +1,95 @@ +'use client' + +import Image from 'next/image' +import { forwardRef, InputHTMLAttributes, useState } from 'react' + +import { cn } from '../lib/cn' + +interface CustomInputProps extends InputHTMLAttributes { + labelName: string + name: string + type?: React.HTMLInputTypeAttribute + autoComplete?: string + placeholder?: string + hasError?: boolean + errorMessage?: string +} + +const Input = forwardRef( + function Input(props, ref) { + const { + labelName, + name, + type = 'text', + placeholder, + hasError, + errorMessage, + autoComplete, + ...rest + } = props + + const [showPassword, setShowPassword] = useState(false) + const isPassword = type === 'password' + const inputType = isPassword && showPassword ? 'text' : type + + return ( + + + {labelName} + + + {isPassword ? ( + + + setShowPassword(!showPassword)} + className="absolute right-16 top-1/2 translate-y-[-50%]" + > + + + + ) : ( + + )} + + {hasError && errorMessage && ( + {errorMessage} + )} + + ) + }, +) + +Input.displayName = 'Input' + +export default Input
{errorMessage}