diff --git a/sign/styles/colors.scss b/sign/styles/colors.scss new file mode 100644 index 000000000..4b6f6e63b --- /dev/null +++ b/sign/styles/colors.scss @@ -0,0 +1,16 @@ +$color-primary: #6d6afe; +$color-red: #ff5b56; +$color-black: #111322; +$color-white: #ffffff; + +$color-gray100: #373740; +$color-gray60: #9fa6b2; +$color-gray20: #ccd5e3; +$color-gray10: #e7effb; +$color-gray-light: #f5f5f5; + +$color-light-blue: #f0f6ff; + +$color-text-gray: #676767; +$color-text-content-gray: #666666; +$color-text-content-black: #333333; diff --git a/sign/styles/global.scss b/sign/styles/global.scss new file mode 100644 index 000000000..7ea93de24 --- /dev/null +++ b/sign/styles/global.scss @@ -0,0 +1,3 @@ +@import "./colors.scss"; +@import "./variables.scss"; +@import "./mixin.scss"; diff --git a/sign/styles/mixin.scss b/sign/styles/mixin.scss new file mode 100644 index 000000000..721cfd6b9 --- /dev/null +++ b/sign/styles/mixin.scss @@ -0,0 +1,24 @@ +@mixin desktop { + @media (min-width: 1200px) { + @content; + } +} + +@mixin tablet { + @media (min-width: 768px) { + @content; + } +} + +@mixin ellipsis($line: 1) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap !important; + + @if $line > 1 { + display: -webkit-box; + -webkit-line-clamp: $line; + white-space: initial !important; + -webkit-box-orient: vertical; + } +} diff --git a/sign/styles/reset.css b/sign/styles/reset.css new file mode 100644 index 000000000..55411320e --- /dev/null +++ b/sign/styles/reset.css @@ -0,0 +1,38 @@ +/* user agent stylesheet 초기화 */ + +* { + box-sizing: border-box; + margin: 0; + font-family: 'Pretendard'; + word-break: keep-all; +} + +html, +body { + font-size: 62.5%; +} + +a { + color: inherit; + text-decoration: none; + cursor: pointer; +} +input { + border: 10px; + padding: none; +} +input:focus { + outline: none; +} +input[type='search']::-webkit-search-decoration, +input[type='search']::-webkit-search-cancel-button, +input[type='search']::-webkit-search-results-button, +input[type='search']::-webkit-search-results-decoration { + display: none; +} +*/ button { + border: none; + padding: unset; + background-color: unset; + cursor: pointer; +} diff --git a/sign/styles/variables.scss b/sign/styles/variables.scss new file mode 100644 index 000000000..3308e4784 --- /dev/null +++ b/sign/styles/variables.scss @@ -0,0 +1,4 @@ +$z-index-popover: 50; +$z-index-nav: 100; +$z-index-fab: 100; +$z-index-modal: 1000; diff --git a/sign/ui-email-input/EmailInput.module.scss b/sign/ui-email-input/EmailInput.module.scss new file mode 100644 index 000000000..e92b1ae48 --- /dev/null +++ b/sign/ui-email-input/EmailInput.module.scss @@ -0,0 +1,53 @@ +@import 'sign/styles/global.scss'; + +$input-width: 400px; + +* { + box-sizing: border-box; +} +.inputField { + width: 100%; + margin-bottom: 20px; + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 16px; + + &:focus { + border-color: #ff5b56; /* 포커스 시 빨간색 border 적용 */ + outline: none; /* 기본 outline 제거 */ + } +} +.input-default { + border: 1px solid #ccd5e3; + width: $input-width; + height: 60px; + font-size: 16px; + outline: none; + border-radius: 8px; + padding: 18px 15px; + &:focus { + border: 1px solid #6d6afe; + } +} + +.input-error { + border: 1px solid #ff5b56; + width: $input-width; + height: 60px; + font-size: 16px; + outline: none; + border-radius: 8px; + padding: 18px 15px; +} + +@media (min-width: 390px) { + .input-default, + .input-error { + width: 325px; + + &:focus { + width: 325px; + } + } +} diff --git a/sign/ui-email-input/EmailInput.tsx b/sign/ui-email-input/EmailInput.tsx new file mode 100644 index 000000000..a9b246f0c --- /dev/null +++ b/sign/ui-email-input/EmailInput.tsx @@ -0,0 +1,26 @@ +import { isEmailValid } from '../utils'; +import React, { useState } from 'react'; +import styles from './EmailInput.module.scss'; +import classNames from 'classnames'; +const cx = classNames.bind(styles); + +const EmailInput = () => { + const [value, setValue] = useState(''); + const isError = value !== '' && !isEmailValid(value); + let classType = isError ? 'input-error' : 'input-default'; + console.log('classType', classType); + const handleInputChange = (event: React.ChangeEvent) => { + setValue(event.target.value); + }; + + return ( + + ); +}; + +export default EmailInput; diff --git a/sign/ui-email-input/index.js b/sign/ui-email-input/index.js new file mode 100644 index 000000000..aaa89b47f --- /dev/null +++ b/sign/ui-email-input/index.js @@ -0,0 +1 @@ +export * from './EmailInput'; diff --git a/sign/ui-password-input/PasswordInput.module.scss b/sign/ui-password-input/PasswordInput.module.scss new file mode 100644 index 000000000..cab9c7e1e --- /dev/null +++ b/sign/ui-password-input/PasswordInput.module.scss @@ -0,0 +1,51 @@ +/* PasswordInput.module.scss */ + +@import 'sign/styles/global.scss'; /* 전역 스타일 import */ + +$button-size: 40px; /* 버튼 크기 변수화 */ + +/* PasswordInput 컴포넌트 스타일 */ +.sign-input-box { + position: relative; + width: 100%; +} + +.input-default { + width: 100%; + height: 60px; + font-size: 16px; + border-radius: 8px; + padding: 18px 15px; + border: 1px solid #ccd5e3; + + &:focus { + border: 1px solid #6d6afe; + outline: none; + } +} + +.input-error { + border: 1px solid #ff5b56; +} + +.eye-button { + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + width: $button-size; + height: $button-size; + background-color: transparent; + border: none; + cursor: pointer; + + svg { + width: 100%; + height: auto; + fill: #000; /* SVG 아이콘 색상 설정 */ + } +} +.eye-button { + border: 0; + background-color: transparent; +} diff --git a/sign/ui-password-input/PasswordInput.tsx b/sign/ui-password-input/PasswordInput.tsx new file mode 100644 index 000000000..d5324c477 --- /dev/null +++ b/sign/ui-password-input/PasswordInput.tsx @@ -0,0 +1,54 @@ +import { useState } from 'react'; +import styles from './PasswordInput.module.scss'; +import { ReactComponent as EyeOff } from './eye-off.svg'; +import { ReactComponent as EyeOn } from './eye-on.svg'; +import { ChangeEventHandler } from 'react'; +import classNames from 'classnames'; +const cx = classNames.bind(styles); + +type PasswordInputProps = { + type: string; + value: string; + onChange?: ChangeEventHandler | undefined; + className?: string; + isPasswordSame: boolean; +}; + +const PasswordInput = ({ + type, + value, + onChange, + isPasswordSame, +}: PasswordInputProps) => { + const [showPassword, setShowPassword] = useState(false); + const [inputType, setInputType] = useState(type); + + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + setInputType(showPassword ? 'password' : 'text'); + }; + + const isError = value && !isPasswordSame; + let classType = !isError ? 'input-default' : 'input-error'; + console.log(classType); + return ( + <> + + + + ); +}; + +export default PasswordInput; diff --git a/sign/ui-password-input/eye-off.svg b/sign/ui-password-input/eye-off.svg new file mode 100644 index 000000000..802730c56 --- /dev/null +++ b/sign/ui-password-input/eye-off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/sign/ui-password-input/eye-on.svg b/sign/ui-password-input/eye-on.svg new file mode 100644 index 000000000..61350f131 --- /dev/null +++ b/sign/ui-password-input/eye-on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/sign/ui-password-input/index.js b/sign/ui-password-input/index.js new file mode 100644 index 000000000..1eed3611f --- /dev/null +++ b/sign/ui-password-input/index.js @@ -0,0 +1 @@ +export * from './PasswordInput'; diff --git a/sign/ui-social-signin/google.svg b/sign/ui-social-signin/google.svg new file mode 100644 index 000000000..867606097 --- /dev/null +++ b/sign/ui-social-signin/google.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/sign/ui-social-signin/kakao.svg b/sign/ui-social-signin/kakao.svg new file mode 100644 index 000000000..4cb7093bc --- /dev/null +++ b/sign/ui-social-signin/kakao.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/sign/utils.js b/sign/utils.js new file mode 100644 index 000000000..bb2919b79 --- /dev/null +++ b/sign/utils.js @@ -0,0 +1,12 @@ +const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g; + +export function isEmailValid(email) { + return new RegExp(EMAIL_REGEX).test(email); +} + +export function isPasswordValid(password) { + const isEightLettersOrMore = password.length >= 8; + const hasNumberAndCharacter = + password.match(/[0-9]/g) && password.match(/[a-zA-Z]/gi); + return isEightLettersOrMore && hasNumberAndCharacter; +}