-
Notifications
You must be signed in to change notification settings - Fork 117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[김민정] Week20 #1086
The head ref may contain hidden characters: "part3-\uAE40\uBBFC\uC815-week20"
[김민정] Week20 #1086
Changes from all commits
db53ca2
8cf15ac
06623af
0500405
946df4e
5358d33
0098a38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,168 +1,59 @@ | ||
import { useState, useEffect, ChangeEvent, KeyboardEvent } from "react"; | ||
import { useState, useEffect } from "react"; | ||
import styled from "styled-components"; | ||
import Image from "next/image"; | ||
import eyeoff from "@/assets/icons/eye-off.png"; | ||
import eyeon from "@/assets/icons/eye-on.png"; | ||
import { changeInputBorderColor } from "@/utils/commonSigninupFunc"; | ||
import { | ||
emailInputValidationcheck, | ||
loginPasswordInputValidationcheck, | ||
signInPasswordInputValidationcheck, | ||
emailDuplicationCheck, | ||
} from "@/utils/validation"; | ||
import { ERROR_MESSAGE } from "@/constants/errorMessage"; | ||
import INPUT_STATUS from "@/constants/inputStatus"; | ||
import eyeoff from "@/public/assets/icons/eye-off.png"; | ||
import eyeon from "@/public/assets/icons/eye-on.png"; | ||
import INPUT_ERROR_INFO from "@/type/inputErrorInfo"; | ||
|
||
|
||
type EmailPwdInputPropsType = { | ||
title: string; | ||
type: string; | ||
valueType: string; | ||
isEyeIcon?: boolean; | ||
setEmailValue?: React.Dispatch<React.SetStateAction<string>>; | ||
emailValue?: string | ""; | ||
setPasswordValue?: React.Dispatch<React.SetStateAction<string | undefined>>; | ||
passwordValue?: string | undefined; | ||
onEnterButtonClick: () => void; | ||
loginStatus?: string; | ||
signInStatus?: string; | ||
setIsEmailValid?: React.Dispatch<React.SetStateAction<boolean>>; | ||
setIsPasswordValid?: React.Dispatch<React.SetStateAction<boolean>>; | ||
setIsPasswordConfirmValid?: React.Dispatch<React.SetStateAction<boolean>>; | ||
eventFunc: { | ||
onFocusOut: () => void; | ||
onChange: () => void; | ||
onKeydown: (e: React.KeyboardEvent<HTMLInputElement>) => void; | ||
} | ||
inputErrorInfo: INPUT_ERROR_INFO; | ||
inputRef: React.RefObject<HTMLInputElement> | ||
}; | ||
|
||
|
||
const EmailPwdInput = ({ | ||
title, | ||
type, | ||
valueType, | ||
isEyeIcon, | ||
setEmailValue, | ||
emailValue, | ||
setPasswordValue, | ||
passwordValue, | ||
onEnterButtonClick, | ||
loginStatus, | ||
signInStatus, | ||
setIsEmailValid, | ||
setIsPasswordValid, | ||
setIsPasswordConfirmValid, | ||
eventFunc, | ||
inputErrorInfo, | ||
inputRef | ||
}: EmailPwdInputPropsType) => { | ||
const INPUT_STATUS_VALUE = { | ||
default: "default", | ||
error: "error", | ||
}; | ||
|
||
const [inputStatus, setInputStatus] = useState("default"); | ||
const [inputErrorMessage, setInputErrorMessage] = useState(""); | ||
|
||
const [isViewPassword, setIsViewPassword] = useState(false); | ||
|
||
const onInputChangeHandler = (e: ChangeEvent<HTMLInputElement>) => { | ||
const inputValue = e.target.value; | ||
if (valueType === "email") { | ||
setEmailValue?.(inputValue); | ||
} else if (valueType === "password") { | ||
setPasswordValue?.(inputValue); | ||
} | ||
}; | ||
|
||
const setInputStatusAndErrorMessage = (status: "valid" | ErrorType) => { | ||
status === "valid" | ||
? setInputStatus(INPUT_STATUS_VALUE.default) | ||
: (setInputStatus(INPUT_STATUS_VALUE.error), | ||
setInputErrorMessage(status.message)); | ||
}; | ||
|
||
const onInputFocusOutHandler = (e: ChangeEvent<HTMLInputElement>) => { | ||
const inputValue = e.target.value; | ||
|
||
switch (valueType) { | ||
case "email": | ||
emailInputFocusOutHandler(inputValue); | ||
break; | ||
case "password": { | ||
passwordInputFocusOutHandler(inputValue); | ||
break; | ||
} | ||
case "password2": | ||
passwordConfirmInputFocusOutHandler(inputValue); | ||
break; | ||
default: | ||
null; | ||
} | ||
}; | ||
|
||
const emailInputFocusOutHandler = async (email: string) => { | ||
const emailInputStatus = emailInputValidationcheck(email); | ||
setInputStatusAndErrorMessage(emailInputStatus); | ||
if (emailInputStatus === "valid") { | ||
setIsEmailValid?.(true); | ||
if (signInStatus) { | ||
const isDuplicateEmail = await emailDuplicationCheck(email); | ||
isDuplicateEmail && | ||
setInputStatusAndErrorMessage(INPUT_STATUS.inUseEmail); | ||
} | ||
} else { | ||
setIsEmailValid?.(false); | ||
} | ||
}; | ||
|
||
const passwordInputFocusOutHandler = async (password: string) => { | ||
let passwordInputStatus; | ||
if (loginStatus) { | ||
passwordInputStatus = loginPasswordInputValidationcheck(password); | ||
} else { | ||
passwordInputStatus = signInPasswordInputValidationcheck(password); | ||
} | ||
setInputStatusAndErrorMessage(passwordInputStatus); | ||
passwordInputStatus === "valid" | ||
? setIsPasswordValid?.(true) | ||
: setIsPasswordValid?.(false); | ||
}; | ||
|
||
const passwordConfirmInputFocusOutHandler = async (password2: string) => { | ||
const passwordInputStatus = signInPasswordInputValidationcheck( | ||
passwordValue, | ||
password2 | ||
); | ||
setInputStatusAndErrorMessage(passwordInputStatus); | ||
passwordInputStatus === "valid" | ||
? setIsPasswordConfirmValid?.(true) | ||
: setIsPasswordConfirmValid?.(false); | ||
}; | ||
|
||
useEffect(() => { | ||
if (loginStatus === "fail") { | ||
valueType === "email" | ||
? (setInputStatus(INPUT_STATUS_VALUE.error), | ||
setInputErrorMessage(ERROR_MESSAGE.email.check)) | ||
: (setInputStatus(INPUT_STATUS_VALUE.error), | ||
setInputErrorMessage(ERROR_MESSAGE.password.check)); | ||
} | ||
}, [loginStatus]); | ||
|
||
const onInputFocusHandler = () => { | ||
setInputStatus("writing"); | ||
}; | ||
|
||
const KeyEventHandler = (e: KeyboardEvent<HTMLInputElement>) => { | ||
const inputElement = e.target as HTMLInputElement; | ||
if (e.key === "Enter") { | ||
onEnterButtonClick(); | ||
inputElement.blur(); | ||
if(inputErrorInfo !== "valid") { | ||
setInputErrorMessage(inputErrorInfo.message); | ||
} else { | ||
setInputErrorMessage(''); | ||
} | ||
}; | ||
|
||
},[inputErrorInfo]) | ||
|
||
return ( | ||
<InputDiv> | ||
<p>{title}</p> | ||
<EmailPasswordInput | ||
type={isViewPassword ? "text" : type} | ||
status={inputStatus} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type 도 string 보다 조금 더 구체적인 타입을 가지거나 기존 Input 의 type 프로퍼티를 확장하면 좋을 거 같아요! |
||
onKeyDown={KeyEventHandler} | ||
onChange={onInputChangeHandler} | ||
onBlur={onInputFocusOutHandler} | ||
onFocus={onInputFocusHandler} | ||
inputErrorInfo={inputErrorInfo} | ||
onChange={eventFunc.onChange} | ||
onBlur={eventFunc.onFocusOut} | ||
onKeyDown={eventFunc.onKeydown} | ||
ref={inputRef} | ||
/> | ||
{isEyeIcon && ( | ||
{type==="password" && ( | ||
<EyeIconDiv | ||
onClick={() => { | ||
setIsViewPassword(!isViewPassword); | ||
|
@@ -171,31 +62,24 @@ const EmailPwdInput = ({ | |
<Image src={isViewPassword ? eyeon : eyeoff} alt="eye_off_icon" /> | ||
</EyeIconDiv> | ||
)} | ||
{inputStatus === "error" && ( | ||
{inputErrorMessage && ( | ||
<ErrorMessageDiv>{inputErrorMessage}</ErrorMessageDiv> | ||
)} | ||
</InputDiv> | ||
); | ||
}; | ||
|
||
type ErrorType = | ||
| { | ||
errorName: string; | ||
type: string; | ||
message: string; | ||
} | ||
| "valid"; | ||
|
||
const InputDiv = styled.div` | ||
width: 100%; | ||
margin-bottom: 24px; | ||
position: relative; | ||
`; | ||
const EmailPasswordInput = styled.input<{ status: string }>` | ||
const EmailPasswordInput = styled.input<{ inputErrorInfo: INPUT_ERROR_INFO }>` | ||
width: 100%; | ||
padding: 18px 15px; | ||
border: ${({ status }) => | ||
`1px solid ${changeInputBorderColor(status) ?? "var(--Grey_300)"}`}; | ||
border: ${({ inputErrorInfo }) => | ||
`1px solid ${inputErrorInfo === "valid" ? "var(--Grey_300)" : "red"}`}; | ||
border-radius: 8px; | ||
outline: none; | ||
margin-top: 12px; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,56 @@ | ||
import styled from "styled-components"; | ||
import { useState } from "react"; | ||
import { CalcTime } from "@/utils/calculator"; | ||
import Star from "@/assets/icons/card_star.svg"; | ||
import Kebab from "@/assets/icons/kebab.svg"; | ||
import logo from "@/assets/icons/logo.png"; | ||
import { calcTime } from "@/utils/calculator"; | ||
import Star from "@/public/assets/icons/card_star.svg"; | ||
import Kebab from "@/public/assets/icons/kebab.svg"; | ||
import logo from "@/public/assets/icons/logo.png"; | ||
import { PopOver } from "./modals/PopOver"; | ||
import Image from "next/image"; | ||
import styles from "@/styles/shared.module.css"; | ||
import Link from "next/link"; | ||
import { CommonFolderInfoProps } from "@/constants/commonTypes"; | ||
import { useModal } from "@/contexts/ModalContext"; | ||
import { DeleteModal } from "./modals/DeleteModal"; | ||
import { AddToFolder } from "./modals/AddToFolder"; | ||
|
||
interface FolderItemProps { | ||
item: CommonFolderInfoProps; | ||
$isModalVisible: string; | ||
setIsModalVisible: any; | ||
} | ||
|
||
function FolderItem({ | ||
item, | ||
$isModalVisible, | ||
setIsModalVisible, | ||
}: FolderItemProps) { | ||
const { imageSource, createdAt, description, url, id } = item; | ||
const { created_at, favorite, image_source } = item; | ||
const [isPopOverVisible, setIsPopOverVisible] = useState(false); | ||
const createdAtTime: string = String(createdAt ?? created_at); | ||
|
||
const time = CalcTime(createdAtTime); | ||
const time = calcTime(createdAtTime); | ||
const img_src = image_source || imageSource; | ||
|
||
const modal = useModal(); | ||
|
||
const openDeleteModal = () => { | ||
modal.openModal(<DeleteModal />); | ||
}; | ||
Comment on lines
+33
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 컴포넌트 자체를 파라미터로 넘기면 나중에 추적할 때 복잡해져서 컴포넌트는 왠만하면 렌더부에만 선언하는 걸 추천드려요! |
||
|
||
const openAddToFolderModal = () => { | ||
modal.openModal(<AddToFolder />) | ||
}; | ||
|
||
const POPOVER_INFO = [ | ||
{ | ||
option: "삭제하기", | ||
callback: openDeleteModal | ||
}, | ||
{ | ||
option: "폴더에 추가", | ||
callback: openAddToFolderModal | ||
} | ||
] | ||
|
||
return ( | ||
<a href={url} target="_blank" rel="noreferrer"> | ||
<Link href={url || './'} target="_blank" rel="noreferrer"> | ||
<Folder> | ||
<ImageContainer> | ||
{img_src ? ( | ||
|
@@ -49,25 +70,21 @@ function FolderItem({ | |
e.preventDefault(); | ||
setIsPopOverVisible(!isPopOverVisible); | ||
}} | ||
/> | ||
/> | ||
<PopOver | ||
$isPopOverVisible={isPopOverVisible} | ||
setIsPopOverVisible={setIsPopOverVisible} | ||
$options={["삭제하기", "폴더에 추가"]} | ||
$modalType={["삭제", "폴더에 추가"]} | ||
popOverInfo={POPOVER_INFO} | ||
$top="20px" | ||
$right="0px" | ||
$isModalVisible={$isModalVisible} | ||
setIsModalVisible={setIsModalVisible} | ||
/> | ||
</TimeContainer> | ||
<Description>{description}</Description> | ||
|
||
<DateText>2023. 3. 15</DateText> | ||
</TextBox>{" "} | ||
* | ||
</Folder> | ||
</a> | ||
</TextBox> | ||
</Folder> | ||
</Link> | ||
); | ||
} | ||
|
||
|
@@ -111,12 +128,13 @@ const DefaultImage = styled.div` | |
|
||
const TextBox = styled.div` | ||
width: 100%; | ||
height: 135px; | ||
height: auto; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 10px; | ||
padding: 15px 20px; | ||
border-radius: 0px 0px 15px 15px; | ||
text-decoration-line: none; | ||
`; | ||
|
||
const Description = styled.div` | ||
|
@@ -129,6 +147,7 @@ const Description = styled.div` | |
line-height: 24px; | ||
margin: 0px; | ||
color: #000; | ||
text-decoration: none; | ||
|
||
display: -webkit-box; | ||
-webkit-box-orient: vertical; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSX.IntrinsicElements['input']
쓰셔도 괜찮을 거 같아요!