Skip to content
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

refactor: 검색창 컴포넌트 개선 및 드롭다운 UI 추가 #551

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/asset/css/SearchBar.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
display: flex;
border-radius: 6.1rem;
background-color: #f2f2f2;
position: relative;
}

.search-bar__wrapper.short {
Expand Down
21 changes: 21 additions & 0 deletions src/asset/css/SearchBarDropDown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.search-bar__dropdown {
text-align: left;
background-color: rgba(255, 255, 255, 0.6) !important;
position: absolute;
top: 6.4rem;
white-space: wrap;
width: calc(100% - 5.4rem);
box-sizing: border-box;
}

@media screen and (max-width: 1200px) {
.search-bar__dropdown {
top: 5.5rem;
}
}

@media screen and (max-width: 767px) {
.search-bar__dropdown {
top: 5rem;
}
}
4 changes: 2 additions & 2 deletions src/component/main/MainBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SearchBar from "../utils/SearchBar";
import BookSearchBar from "../utils/BookSearchBar";
import "../../asset/css/Banner.css";
import "../../asset/css/MainBanner.css";

Expand All @@ -18,7 +18,7 @@ const MainBanner = () => {
<span className="main-banner__guide2 font-16 color-d5">
검색창에 원하는 도서를 입력해주세요.
</span>
<SearchBar width="banner" isNavigate />
<BookSearchBar />
</div>
<div className="main-banner__scroll">
<p className="font-12 color-d5">스크롤을 내려주세요</p>
Expand Down
8 changes: 2 additions & 6 deletions src/component/search/SearchBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SearchBar from "../utils/SearchBar";
import BookSearchBar from "../utils/BookSearchBar";
import "../../asset/css/Banner.css";
import "../../asset/css/SearchBanner.css";

Expand All @@ -18,11 +18,7 @@ const SearchBanner = ({ setQuery }: Props) => {
SEARCH
</span>
</div>
<SearchBar
setQuery={setQuery}
width="banner"
isFocusedOnMount={false}
/>
<BookSearchBar />
</section>
</section>
);
Expand Down
4 changes: 2 additions & 2 deletions src/component/superTag/SuperTagMergeDefaultTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DragEventHandler, useState } from "react";
import { usePatchTagsBookInfoIdMerge } from "../../api/tags/usePatchTagsBookInfoIdMerge";
import { Tag } from "../../type";
import Accordion from "../utils/Accordion";
import SearchBar from "../utils/SearchBar";
import ManagementSearchBar from "../utils/ManagementSearchBar";
import Droppable from "../utils/Droppable";
import SuperTagMergeSubTag from "./SuperTagMergeSubTag";

Expand Down Expand Up @@ -49,7 +49,7 @@ const SuperTagMergeDefaultTag = ({
format="text/plain"
onDrop={addNewListAndMergeIfMoved}
>
<SearchBar
<ManagementSearchBar
wrapperClassName="super-tag__default__search-bar"
width="short"
placeHolder="분류없음 내 검색"
Expand Down
4 changes: 2 additions & 2 deletions src/component/userManagement/UserManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Banner from "../utils/Banner";
import Tabs from "../utils/Tabs";
import UserBriefInfo from "./UserBriefInfo";
import UserUsageInfo from "./UserUsageInfo";
import SearchBar from "../utils/SearchBar";
import ManagementSearchBar from "../utils/ManagementSearchBar";
import Pagination from "../utils/Pagination";
import Modal from "../utils/Modal";
import ModalHeader from "../utils/ModalHeader";
Expand Down Expand Up @@ -32,7 +32,7 @@ const UserManagement = () => {
<Tabs tabList={userManagementTabList} />
<section className="user-management-body">
<div className="user-management-search">
<SearchBar
<ManagementSearchBar
width="center"
placeHolder="nickname 또는 email을 입력해주세요."
setQuery={setQuery}
Expand Down
50 changes: 50 additions & 0 deletions src/component/utils/BookSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { FormEventHandler, useEffect, useRef, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import SearchBar from "~/component/utils/SearchBar";
import BookSearchPreview from "~/component/utils/BookSearchPreview";
import BookSearchRecentKeyword from "~/component/utils/BookSearchRecentKeywords";

const BookSearchBar = () => {
const [isOpened, setIsOpened] = useState(false);
const [keyword, setKeyword] = useState("");
const [params] = useSearchParams();
const ref = useRef<HTMLInputElement>(null);
const navigate = useNavigate();

const goToSearchPage: FormEventHandler<HTMLFormElement> = e => {
e.preventDefault();
const searchWord = e.currentTarget.input.value;
navigate(`/search?search=${encodeURIComponent(searchWord)}`);
e.currentTarget.input.blur();
};

useEffect(() => {
const currentKeyword = params.get("search");
setKeyword(currentKeyword || "");
}, [params]);

return (
<SearchBar onSubmit={goToSearchPage}>
<SearchBar.Input
value={keyword}
onChange={e => setKeyword(e.target.value)}
onFocus={() => setIsOpened(true)}
ref={ref}
/>
<SearchBar.DropDown
isOpened={isOpened}
setIsOpened={setIsOpened}
searchBarRef={ref}
>
{keyword.length == 0 ? (
<BookSearchRecentKeyword />
) : (
<BookSearchPreview />
)}
</SearchBar.DropDown>
<SearchBar.Button />
</SearchBar>
);
};

export default BookSearchBar;
5 changes: 5 additions & 0 deletions src/component/utils/BookSearchPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const BookSearchPreview = () => {
return <div>결과 미리보기</div>;
};

export default BookSearchPreview;
5 changes: 5 additions & 0 deletions src/component/utils/BookSearchRecentKeywords.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const BookSearchRecentKeyword = () => {
return <div>최근 검색어</div>;
};

export default BookSearchRecentKeyword;
4 changes: 2 additions & 2 deletions src/component/utils/InquireBoxTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from "./Image";
import SearchBar from "./SearchBar";
import ManagementSearchBar from "./ManagementSearchBar";
import "../../asset/css/InquireBoxTitle.css";

type Props = {
Expand Down Expand Up @@ -51,7 +51,7 @@ const InquireBoxTitle = ({
</span>
</span>
{placeHolder ? (
<SearchBar
<ManagementSearchBar
placeHolder={placeHolder}
width="short"
setQuery={setQuery}
Expand Down
4 changes: 2 additions & 2 deletions src/component/utils/Management.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SearchBar from "./SearchBar";
import ManagementSearchBar from "./ManagementSearchBar";
import Pagination from "./Pagination";
import "../../asset/css/Management.css";

Expand All @@ -23,7 +23,7 @@ const Management = ({
}: Props) => {
return (
<section className="management__wrapper">
<SearchBar
<ManagementSearchBar
placeHolder={searchBarPlaceHolder}
width="center"
wrapperClassName="management__search-bar"
Expand Down
68 changes: 68 additions & 0 deletions src/component/utils/ManagementSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
ChangeEventHandler,
MouseEventHandler,
useEffect,
useRef,
useState,
} from "react";
import SearchBar from "~/component/utils/SearchBar";

type Props = {
width: "banner" | "center" | "short" | "long";
setQuery?: (query: string) => void;
placeHolder?: string;
wrapperClassName?: string;
isWithBarcodeButton?: boolean;
isFocusedOnMount?: boolean;
onClickBarcodeButton?: MouseEventHandler<HTMLButtonElement>;
};

const ManagementSearchBar = ({
width,
setQuery = () => {},
placeHolder,
wrapperClassName = "",
isWithBarcodeButton = false,
isFocusedOnMount = true,
onClickBarcodeButton = () => {},
...rest
}: Props) => {
const [keyword, setKeyword] = useState("");
const searchRef = useRef<HTMLInputElement>(null);

useEffect(() => {
if (isFocusedOnMount && searchRef.current) {
searchRef.current.focus();
}
}, []);

const changeKeyword: ChangeEventHandler<HTMLInputElement> = e => {
const changed = e.currentTarget.value;
setKeyword(changed);
setQuery(changed);
};

return (
<SearchBar className={wrapperClassName} width={width}>
<SearchBar.Input
{...rest}
placeholder={placeHolder}
value={keyword}
onChange={changeKeyword}
ref={searchRef}
/>
{isWithBarcodeButton ? (
<button
type="button"
className="search-bar__button barcode"
onClick={onClickBarcodeButton}
>
바코드
</button>
) : null}
<SearchBar.Button />
</SearchBar>
);
};

export default ManagementSearchBar;
98 changes: 17 additions & 81 deletions src/component/utils/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,28 @@
import {
ChangeEventHandler,
FormEventHandler,
MouseEventHandler,
useEffect,
useRef,
useState,
} from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import "../../asset/css/SearchBar.css";
import BarCodeIcon from "../../asset/img/barcode.svg";
import SearchIcon from "../../asset/img/search_icon_black.svg";
import { ComponentProps } from "react";
import SearchBarInput from "~/component/utils/SearchBarInput";
import SearchBarButton from "~/component/utils/SearchBarButton";
import SearchBarDropDown from "~/component/utils/SearchBarDropDown";
import "~/asset/css/SearchBar.css";

type Props = {
setQuery?: (query: string) => void;
placeHolder?: string;
wrapperClassName?: string;
width: "banner" | "center" | "short" | "long";
isWithBarcodeButton?: boolean;
onClickBarcodeButton?: MouseEventHandler<HTMLButtonElement>;
isNavigate?: boolean;
isFocusedOnMount?: boolean;
export type Props = ComponentProps<"form"> & {
width?: "banner" | "center" | "short" | "long";
};

const SearchBar = ({
setQuery,
placeHolder = "",
wrapperClassName = "",
width,
isWithBarcodeButton = false,
onClickBarcodeButton = () => {},
isNavigate = false,
isFocusedOnMount = true,
width = "banner",
className = "",
children,
...rest
}: Props) => {
const [urlParams] = useSearchParams();
const [searchWord, setSearchWord] = useState(urlParams.get("search") || "");
const navigate = useNavigate();
const searchRef = useRef<HTMLInputElement>(null);

const onChange: ChangeEventHandler<HTMLInputElement> = event => {
const { value } = event.currentTarget;
if (typeof setQuery === "function") {
if (!isNavigate) setQuery(value);
}
setSearchWord(value);
};

const onSubmit: FormEventHandler = event => {
event.preventDefault();
const encodedSearchWord = encodeURIComponent(searchWord);
if (isNavigate) navigate(`/search?search=${encodedSearchWord}`);
};

useEffect(() => {
if (isFocusedOnMount && searchRef.current) {
searchRef.current.focus();
}
}, []);

return (
<form
className={`search-bar__wrapper ${width} ${wrapperClassName}`}
onSubmit={onSubmit}
>
<input
className="search-bar__input"
required
type="text"
autoComplete="off"
placeholder={placeHolder}
value={searchWord}
onChange={onChange}
ref={searchRef}
/>
{isWithBarcodeButton ? (
<button
type="button"
className="search-bar__button barcode"
onClick={onClickBarcodeButton}
aria-label="바코드"
>
<img src={BarCodeIcon} alt="" />
</button>
) : null}
<button className="search-bar__button" type="submit" aria-label="검색">
<img src={SearchIcon} alt="" />
</button>
<form {...rest} className={`search-bar__wrapper ${width} ${className}`}>
{children}
</form>
);
};

export default SearchBar;

SearchBar.Input = SearchBarInput;
SearchBar.Button = SearchBarButton;
SearchBar.DropDown = SearchBarDropDown;
12 changes: 12 additions & 0 deletions src/component/utils/SearchBarButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SearchIcon from "~/asset/img/search_icon_black.svg";
import Image from "~/component/utils/Image";

const SearchBarButton = () => {
return (
<button className="search-bar__button" type="submit" aria-label="검색">
<Image className="search-bar__button" src={SearchIcon} alt="" />
</button>
);
};

export default SearchBarButton;
Loading