Skip to content

Commit

Permalink
UI: #127 수행자 검색 UI 컴포넌트 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
Seok93 committed Sep 14, 2024
1 parent 536d70b commit 30158b6
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 49 deletions.
73 changes: 73 additions & 0 deletions src/components/common/SearchUserInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { IoSearch } from 'react-icons/io5';
import type { UserWithRole } from '@/types/UserType';

type SearchInputProps = {
id: string;
label: string;
keyword: string;
loading: boolean;
userList: UserWithRole[];
onKeywordChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
onSearchKeyup: (event: React.KeyboardEvent<HTMLInputElement>) => void;
onSearchClick: () => void;
onUserClick: (user: UserWithRole) => void;
};

export default function SearchUserInput({
id,
label,
keyword,
loading,
userList,
onKeywordChange: handleKeywordChange,
onSearchKeyup: handleSearchKeyUp,
onSearchClick: handleSearchClick,
onUserClick: handleUserClick,
}: SearchInputProps) {
return (
<label htmlFor={id} className="group mb-10 flex items-center gap-5">
<h3 className="text-large">{label}</h3>
<section className="relative grow">
<input
type="text"
id={id}
className="h-25 w-full rounded-md border border-input pl-10 pr-25 text-regular placeholder:text-xs"
value={keyword}
onChange={handleKeywordChange}
onKeyDown={handleSearchKeyUp}
placeholder="닉네임을 검색해주세요."
/>
<button
type="button"
aria-label="search"
className="absolute right-5 top-1/2 -translate-y-1/2 cursor-pointer"
onClick={handleSearchClick}
>
<IoSearch className="size-15 text-emphasis hover:text-black" />
</button>
{keyword && !loading && (
<ul className="invisible absolute left-0 right-0 z-10 max-h-110 overflow-auto rounded-md border-2 bg-white group-focus-within:visible">
{userList.length === 0 ? (
<div className="h-20 border px-10 leading-8">&apos;{keyword}&apos; 의 검색 결과가 없습니다.</div>
) : (
userList?.map((user) => (
<li className="h-20 border" key={user.userId}>
<button
type="button"
className="h-full w-full px-10 text-left hover:bg-sub"
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.blur();
handleUserClick(user);
}}
>
{user.nickname}
</button>
</li>
))
)}
</ul>
)}
</section>
</label>
);
}
63 changes: 14 additions & 49 deletions src/components/modal/task/ModalTaskForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { DateTime } from 'luxon';
import { FormProvider, useForm } from 'react-hook-form';
import { IoSearch } from 'react-icons/io5';
import { IoMdCloseCircle } from 'react-icons/io';

import { TASK_SETTINGS } from '@constants/settings';
Expand All @@ -12,6 +11,7 @@ import StatusRadio from '@components/common/StatusRadio';
import FileDropZone from '@components/common/FileDropZone';
import MarkdownEditor from '@components/common/MarkdownEditor';
import PeriodDateInput from '@components/common/PeriodDateInput';
import SearchUserInput from '@components/common/SearchUserInput';
import DuplicationCheckInput from '@components/common/DuplicationCheckInput';
import useToast from '@hooks/useToast';
import useAxios from '@hooks/useAxios';
Expand Down Expand Up @@ -45,7 +45,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod

const { statusList, isStatusLoading } = useReadStatuses(projectId, taskId);
const { taskNameList } = useReadStatusTasks(projectId);
const { data, loading, clearData, fetchData } = useAxios(findUserByProject);
const { data = [], loading, clearData, fetchData } = useAxios(findUserByProject);
const { toastInfo, toastWarn } = useToast();

// ToDo: 상태 수정 모달 작성시 기본값 설정 방식 변경할 것
Expand Down Expand Up @@ -85,9 +85,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
}, [isStatusLoading, statusList]);

useEffect(() => {
if (keyword) {
debounceRef.current = setTimeout(() => searchUsers(), 500);
}
if (keyword) debounceRef.current = setTimeout(() => searchUsers(), 500);
return () => {
if (debounceRef.current) clearTimeout(debounceRef.current);
if (abortControllerRef.current) abortControllerRef.current.abort();
Expand Down Expand Up @@ -189,50 +187,17 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod

{/* ToDo: 검색UI 공용 컴포넌트로 추출할 것 */}
<div className="mb-20">
<label htmlFor="search" className="group mb-10 flex items-center gap-5">
<h3 className="text-large">수행자</h3>
<section className="relative grow">
<input
type="text"
id="search"
className="h-25 w-full rounded-md border border-input pl-10 pr-25 text-regular placeholder:text-xs"
value={keyword}
onChange={handleKeywordChange}
onKeyDown={handleSearchKeyUp}
placeholder="닉네임을 검색해주세요."
/>
<button
type="button"
aria-label="search"
className="absolute right-5 top-1/2 -translate-y-1/2 cursor-pointer"
onClick={handleSearchClick}
>
<IoSearch className="size-15 text-emphasis hover:text-black" />
</button>
{keyword && !loading && (
<ul className="invisible absolute left-0 right-0 z-10 max-h-110 overflow-auto rounded-md border-2 bg-white group-focus-within:visible">
{data && data.length === 0 ? (
<div className="h-20 border px-10 leading-8">&apos;{keyword}&apos; 의 검색 결과가 없습니다.</div>
) : (
data?.map((user) => (
<li className="h-20 border" key={user.userId}>
<button
type="button"
className="h-full w-full px-10 text-left hover:bg-sub"
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.blur();
handleUserClick(user);
}}
>
{user.nickname}
</button>
</li>
))
)}
</ul>
)}
</section>
</label>
<SearchUserInput
id="search"
label="수행자"
keyword={keyword}
loading={loading}
userList={data}
onKeywordChange={handleKeywordChange}
onSearchKeyup={handleSearchKeyUp}
onSearchClick={handleSearchClick}
onUserClick={handleUserClick}
/>
<section className="flex w-full flex-wrap items-center gap-4">
{workers.map((user) => (
<div key={user.userId} className="flex items-center space-x-4 rounded-md bg-button px-5">
Expand Down

0 comments on commit 30158b6

Please sign in to comment.