Skip to content

Commit

Permalink
refactor: 공용 인풋 피드백 반영 (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
21ow authored Dec 26, 2024
1 parent 6f45895 commit 1a545c8
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 63 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@mui/material": "^6.2.0",
"@svgr/webpack": "^8.1.0",
"axios": "^1.7.9",
"classnames": "^2.5.1",
"next": "15.0.3",
"react": "18.3.1",
"react-dom": "18.3.1",
Expand Down
9 changes: 5 additions & 4 deletions src/hook/useTextCounter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
import { useState } from 'react';

const useTextCounter = (maxLength: number | undefined) => {
const [text, setText] = useState<string>('');
const [text, setText] = useState<number>(0);

const handleTextCounter = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
if (!maxLength) return;

const userText = e.target.value;
if (userText.length <= maxLength) {
const userText = e.target.value.length;
console.log(userText);
if (userText <= maxLength) {
setText(userText);
//한국어 입력 시 초과 글자는 포커스 아웃해야 사라짐
}
};
//https://yungis.dev/react/textarea-maxlength-limit/

return { text, handleTextCounter };
};
Expand Down
2 changes: 1 addition & 1 deletion src/shared/Input/Input.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
}
}

.pwWrapper {
.inputWrapper {
position: relative;
}

Expand Down
56 changes: 20 additions & 36 deletions src/shared/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
'use client';

import React, { forwardRef } from 'react';
import useInput from './hook/useInput';
import classNames from 'classnames';
import { useId } from 'react';
import usePasswordToggle from './hook/usePasswordToggle';
import useImageUpload from './hook/useImageUpload';
import useTextCounter from '@/hook/useTextCounter';
import Label from './component/Label';
import PwToggleBtn from './component/PwToggleBtn';
import TextCount from '../TextCount';
// import InfoMessage from './component/InfoMessage';
import ErrorMessage from './component/ErrorMessage';
import FilePreview from './component/FilePreview';
import styles from './Input.module.scss';

type InputProps = {
type: string;
id: string;
label?: string;
maxLength?: number;
multiple?: boolean;
formErrorMessage?: string | null;
className?: string;
} & React.InputHTMLAttributes<HTMLInputElement>;

const Input = forwardRef<HTMLInputElement | null, InputProps>(
(
{
type,
id,
type = 'text',
label,
maxLength,
multiple,
Expand All @@ -35,32 +31,24 @@ const Input = forwardRef<HTMLInputElement | null, InputProps>(
},
ref
) => {
const { inputRef, showPassword, handleTogglePassword } =
usePasswordToggle();

const {
showPassword,
filePreviews,
inputKey,
fileErrorMessage,
handleTogglePassword,
handleFileChange,
handleDeleteImg,
} = useInput(multiple);
} = useImageUpload(multiple);

const id = useId();

const { text, handleTextCounter } = useTextCounter(maxLength);

const handleChange = (
e: React.ChangeEvent<HTMLInputElement>,
type: string,
id: string
) => {
const handlers: {
[key: string]: (e: React.ChangeEvent<HTMLInputElement>) => void;
} = {
file: handleFileChange,
text_nickname: handleTextCounter,
};
const key = type === 'text' && id === 'nickname' ? 'text_nickname' : type;
handlers[key]?.(e);
};
const inputClassName = classNames(styles.input, className, {
[styles.errorStatus]: formErrorMessage,
});

return (
<div
Expand All @@ -71,20 +59,18 @@ const Input = forwardRef<HTMLInputElement | null, InputProps>(
}
>
<Label type={type} id={id} label={label} />
<div className={styles.pwWrapper}>
<div className={styles.inputWrapper}>
<input
key={inputKey}
type={showPassword ? 'text' : type}
id={id}
ref={ref}
ref={type === 'file' ? ref : inputRef}
maxLength={maxLength}
onChange={(e) => handleChange(e, type, id)}
multiple={multiple}
className={
formErrorMessage
? `${styles.input} ${className} ${styles.errorStatus}`
: `${styles.input} ${className}`
onChange={(e) =>
type === 'file' ? handleFileChange(e) : handleTextCounter(e)
}
multiple={multiple}
className={inputClassName}
{...props}
/>
<PwToggleBtn
Expand All @@ -99,8 +85,6 @@ const Input = forwardRef<HTMLInputElement | null, InputProps>(
/>
</div>

{/* 디자인 변경 고려*/}
{/* <InfoMessage id={id} /> */}
<ErrorMessage
formError={formErrorMessage}
fileError={fileErrorMessage}
Expand Down
4 changes: 2 additions & 2 deletions src/shared/Input/component/PwToggleBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ const PwToggleBtn = ({
>
{showPassword ? (
<Image
src={'/icon/ic-invisibility.svg'}
src={'/icon/ic-visibility.svg'}
alt={'비밀번호 숨기기 버튼'}
width={24}
height={24}
/>
) : (
<Image
src={'/icon/ic-visibility.svg'}
src={'/icon/ic-invisibility.svg'}
alt={'비밀번호 보기 버튼'}
width={24}
height={24}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@

import { useState } from 'react';

const useInput = (multiple: boolean | undefined) => {
const [showPassword, setShowPassword] = useState(false);
const [filePreviews, setFilePreviews] = useState<string[]>([]);
const useImageUpload = (multiple: boolean | undefined) => {
const [inputKey, setInputKey] = useState<number>(0);
const [filePreviews, setFilePreviews] = useState<string[]>([]);
const [fileErrorMessage, setFileErrorMessage] = useState<string | null>(null);

const MAX_FILES = multiple ? 4 : 1;

const handleTogglePassword = () => {
setShowPassword((prev) => !prev);
};

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (!files) return;
Expand Down Expand Up @@ -72,14 +67,12 @@ const useInput = (multiple: boolean | undefined) => {
};

return {
showPassword,
filePreviews,
inputKey,
fileErrorMessage,
handleTogglePassword,
handleFileChange,
handleDeleteImg,
};
};

export default useInput;
export default useImageUpload;
23 changes: 23 additions & 0 deletions src/shared/Input/hook/usePasswordToggle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import { useState, useRef } from 'react';

const usePasswordToggle = () => {
const [showPassword, setShowPassword] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);

const handleTogglePassword = () => {
setShowPassword((prev) => !prev);
if (inputRef.current) {
inputRef.current.focus();
}
};

return {
inputRef,
showPassword,
handleTogglePassword,
};
};

export default usePasswordToggle;
13 changes: 5 additions & 8 deletions src/shared/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@ import TextCount from '../TextCount';
import useTextCounter from '@/hook/useTextCounter';
import styles from './TextArea.module.scss';

type TextAreaProps = {
id: string;
className?: string;
maxLength: number;
} & React.TextareaHTMLAttributes<HTMLTextAreaElement>;

const TextArea = ({ id, className, maxLength, ...props }: TextAreaProps) => {
const TextArea = ({
className,
maxLength,
...props
}: React.TextareaHTMLAttributes<HTMLTextAreaElement>) => {
const { text, handleTextCounter } = useTextCounter(maxLength);
return (
<div className={styles.textareaWrapper}>
<textarea
id={id}
maxLength={maxLength}
onChange={handleTextCounter}
{...props}
Expand Down
4 changes: 2 additions & 2 deletions src/shared/TextCount.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

type TextCountProps = {
text: string;
text: number;
maxLength?: number;
className?: string;
};
Expand All @@ -10,7 +10,7 @@ const TextCount = ({ text, maxLength, className }: TextCountProps) => {
return (
maxLength && (
<div className={className}>
{text.length}/{maxLength}
{text}/{maxLength}
</div>
)
);
Expand Down

0 comments on commit 1a545c8

Please sign in to comment.