Skip to content

Commit ae6e3bb

Browse files
authored
Merge pull request #57 from FE9-2/feat/Input
Design : text_field & text_area UI 구현 완료
2 parents 58b5bb4 + c1cd494 commit ae6e3bb

19 files changed

+303
-25
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
import { BaseFileInputProps } from "@/types/textInput";
4+
import { useRef } from "react";
5+
6+
const BaseFileInput = (props: BaseFileInputProps) => {
7+
const colorStype = {
8+
bgColor: "bg-background-200",
9+
borderColor: "border border-transparent",
10+
hoverColor: "hover:border-gray-200 hover:bg-background-300",
11+
focusColor: "[&:has(input:focus)]:border-primary-orange-300 caret-primary-orange-300",
12+
innerHoverColor: "hover:bg-background-300",
13+
};
14+
15+
const defaultSize = "w-[327px] h-[54px] lg:w-[640px] lg:h-[64px]";
16+
const sizeStyles = props.size || defaultSize;
17+
18+
const wrapperColorStyle = `${colorStype.bgColor} ${colorStype.borderColor} ${colorStype.hoverColor} ${colorStype.focusColor}`;
19+
const wrapperStyle = `relative flex gap-2 items-center justify-between rounded-lg border-[0.5px] p-[14px] lg:py-4 ${wrapperColorStyle} ${sizeStyles}`;
20+
21+
const innerColorStyle = `${colorStype.innerHoverColor}`;
22+
const fakeInputStyle = `text-gray-400 flex items-center border-none ${innerColorStyle}`;
23+
24+
// 라벨 클릭 시 input 클릭 - 파일 선택 창 열기 / 파일 다운로드
25+
const inputRef = useRef<HTMLInputElement>(null);
26+
const handleWrapperClick = () => {
27+
if (props.variant === "upload") {
28+
inputRef.current?.click();
29+
} else {
30+
// 다운로드?
31+
}
32+
};
33+
34+
return (
35+
<>
36+
<div className={wrapperStyle} onClick={props.variant === "upload" ? handleWrapperClick : undefined}>
37+
<label htmlFor={props.name} className={fakeInputStyle}>
38+
{props.file ? props.file.name : props.placeholder}
39+
</label>
40+
{props.variant === "upload" && (
41+
<input
42+
id={props.name}
43+
type="file"
44+
onChange={(e) => props.onFileAction?.(e.target.files?.[0] || null)}
45+
ref={inputRef}
46+
className="hidden"
47+
/>
48+
)}
49+
{props.variant === "upload" && props.actionIcon}
50+
{props.variant === "download" && props.icon}
51+
</div>
52+
</>
53+
);
54+
};
55+
56+
export default BaseFileInput;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { FiDownload } from "react-icons/fi";
2+
import BaseFileInput from "./BaseFileInput";
3+
import { BaseFileInputProps } from "@/types/textInput";
4+
5+
const DownloadInput = (props: BaseFileInputProps) => {
6+
return (
7+
<BaseFileInput {...props} variant="download" icon={<FiDownload className="size-4 text-black-400 lg:size-6" />} />
8+
);
9+
};
10+
11+
export default DownloadInput;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { HiUpload } from "react-icons/hi";
2+
import { useState } from "react";
3+
import { IoCloseCircleOutline } from "react-icons/io5";
4+
import BaseFileInput from "./BaseFileInput";
5+
import { BaseFileInputProps } from "@/types/textInput";
6+
import { useFile } from "@/hooks/useFile";
7+
8+
const UploadInput = (props: BaseFileInputProps) => {
9+
const { file, handleChangeFile, handleDeleteFile } = useFile();
10+
11+
return (
12+
<BaseFileInput
13+
{...props}
14+
file={file}
15+
placeholder="파일 업로드하기"
16+
onFileAction={handleChangeFile}
17+
actionIcon={
18+
file ? (
19+
<button type="button" onClick={handleDeleteFile}>
20+
<IoCloseCircleOutline className="size-4 text-gray-400 lg:size-6" />
21+
</button>
22+
) : (
23+
<HiUpload className="size-4 text-black-400 lg:size-6" />
24+
)
25+
}
26+
/>
27+
);
28+
};
29+
30+
export default UploadInput;

src/app/components/input/BaseInput.tsx renamed to src/app/components/input/text/BaseInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,23 @@ const BaseInput = (props: BaseInputProps) => {
4141
const errorTextStyle =
4242
"absolute -bottom-[26px] text-[13px] text-sm font-medium leading-[22px] text-state-error lg:text-base lg:leading-[26px]";
4343

44-
const wrapperStyle = `relative flex items-center justify-between rounded-lg border-[0.5px] p-[14px] lg:py-4 ${variantStyle} ${sizeStyles} ${errorStyle} ${props.wrapperClassName}`;
44+
const wrapperStyle = `relative flex gap-2 items-center justify-between rounded-lg border-[0.5px] p-[14px] lg:py-4 ${variantStyle} ${sizeStyles} ${errorStyle} ${props.wrapperClassName}`;
4545
const inputStyle = `bg-transparent ${baseStyle} ${textStyle} ${props.innerClassName}`;
4646
return (
4747
<div>
4848
<div className={wrapperStyle}>
49-
{props.beforeIcon && <div className="absolute left-0">{props.beforeIcon}</div>}
49+
{props.beforeIcon && <div className="flex items-center justify-center">{props.beforeIcon}</div>}
5050
<label className="hidden">{props.name}</label>
5151
<input id={props.name} type={inputType} placeholder={props.placeholder} className={inputStyle} />
5252
{props.type === "password" && (
5353
<div onClick={toggleType} className="cursor-pointer">
5454
{eyeOn ? <LuEye className="text-gray-200" /> : <LuEyeOff className="text-gray-200" />}
5555
</div>
5656
)}
57+
{props.afterIcon && <div>{props.afterIcon}</div>}
5758
{props.errorMessage && <span className={`${errorTextStyle} right-0 pr-2`}>{props.errorMessage}</span>}
5859
{props.feedbackMessage && <span className={`${errorTextStyle} left-0 pl-2`}>{props.feedbackMessage}</span>}
5960
</div>
60-
{props.afterIcon && <div className="absolute right-0">{props.afterIcon}</div>}
6161
</div>
6262
);
6363
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { TextInputProps } from "@/types/textInput";
5+
import { IoLocationSharp } from "react-icons/io5";
6+
import WhiteTextInput from "./WhiteTextInput";
7+
8+
const LocationInput = ({ type, ...props }: TextInputProps) => {
9+
const [errorMessage, setErrorMessage] = useState("");
10+
const [feedbackMessage, setFeedbackMessage] = useState("");
11+
return (
12+
<WhiteTextInput
13+
type="text"
14+
beforeIcon={<IoLocationSharp className="size-4 text-gray-100 lg:size-6" />}
15+
placeholder="위치를 입력해주세요."
16+
errorMessage={errorMessage}
17+
feedbackMessage={feedbackMessage}
18+
{...props}
19+
/>
20+
);
21+
};
22+
23+
export default LocationInput;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { FiSearch } from "react-icons/fi";
2+
import WhiteTextInput from "./WhiteTextInput";
3+
4+
const SearchInput = () => {
5+
return (
6+
<div>
7+
<WhiteTextInput
8+
name="search"
9+
type="text"
10+
placeholder="어떤 알바를 찾고 계세요?"
11+
wrapperClassName="!rounded-2xl !lg:rounded-3xl"
12+
beforeIcon={<FiSearch className="size-4 text-gray-200 lg:size-6" />}
13+
/>
14+
</div>
15+
);
16+
};
17+
18+
export default SearchInput;

src/app/components/input/TransperTextInput.tsx renamed to src/app/components/input/text/TransperTextInput.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1+
import { TextInputProps } from "@/types/textInput";
12
import BaseInput from "./BaseInput";
2-
import { BaseInputProps } from "@/types/textInput";
3-
4-
type TransperTextInputProps = Omit<BaseInputProps, "variant">;
5-
6-
const TransperTextInput = (props: TransperTextInputProps) => {
3+
const TransperTextInput = (props: TextInputProps) => {
74
return <BaseInput {...props} variant="transparent" />;
85
};
96

src/app/components/input/WhiteTextInput.tsx renamed to src/app/components/input/text/WhiteTextInput.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1+
import { TextInputProps } from "@/types/textInput";
12
import BaseInput from "./BaseInput";
2-
import { BaseInputProps } from "@/types/textInput";
33

4-
type TransperTextInputProps = Omit<BaseInputProps, "variant">;
5-
6-
const TransperTextInput = (props: TransperTextInputProps) => {
4+
const TransperTextInput = (props: TextInputProps) => {
75
return <BaseInput {...props} variant="white" />;
86
};
97

@@ -12,7 +10,7 @@ export default TransperTextInput;
1210
/*
1311
@name: string // 필수값
1412
@type: "text" | "password" | ... // 필수값
15-
@size: "w-[00px] h-[00px] lg:w-[00px] lg:h-[00px]" // 기본값: "w-[327px] h-[54px] lg:w-[640px] lg:h-[64px]"
13+
@size: "w-[00px] h-[00px] lg:w-[00px] lg:h-[00px]"
1614
@placeholder: string
1715
@errorMessage: string // 에러메시지 + 테두리 색상 변경
1816
@feedbackMessage: string // 메시지만 띄우고 색상 변경 X
File renamed without changes.

src/app/components/textarea/TransparentTextArea.tsx renamed to src/app/components/input/textarea/TransparentTextArea.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import BaseTextArea from "./BaseTextArea";
2-
import { BaseTextAreaProps } from "@/types/textInput";
2+
import { TextAreaProps } from "@/types/textInput";
33

4-
type TransparentTextAreaProps = Omit<BaseTextAreaProps, "variant">;
5-
6-
const TransparentTextArea = (props: TransparentTextAreaProps) => {
4+
const TransparentTextArea = (props: TextAreaProps) => {
75
return <BaseTextArea {...props} variant="transparent" />;
86
};
97

0 commit comments

Comments
 (0)