From da52a78dbcb91ad232d9b54a8fd0a784269bb7be Mon Sep 17 00:00:00 2001 From: zoonyoung <9851248@gmail.com> Date: Sun, 28 Apr 2024 18:28:42 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20Search=EC=97=90=20autoSuggestion?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/Header/SearchBox.module.scss | 32 +++++++ src/components/common/Header/SearchBox.tsx | 90 +++++++++++++++++-- tsconfig.json | 1 + 3 files changed, 115 insertions(+), 8 deletions(-) diff --git a/src/components/common/Header/SearchBox.module.scss b/src/components/common/Header/SearchBox.module.scss index 4a0cbaa..2dbc8aa 100644 --- a/src/components/common/Header/SearchBox.module.scss +++ b/src/components/common/Header/SearchBox.module.scss @@ -40,3 +40,35 @@ width: 100%; } } + +.itemBox { + position: absolute; + display: flex; + flex-direction: column; + top: 5rem; + left: 0; + z-index: 1; + width: 100%; + overflow: hidden; + background: $white; + border: 2px solid $red-200; + + @include rounded-lg; +} + +.item { + padding: 0.6rem 1.5rem; + font-size: 1.6rem; + border: none; + background: none; + cursor: pointer; + border: 1px solid $red-600; + + span { + @include font-bold; + } + + &:hover { + background: $red-600; + } +} diff --git a/src/components/common/Header/SearchBox.tsx b/src/components/common/Header/SearchBox.tsx index 2e353ed..05f3167 100644 --- a/src/components/common/Header/SearchBox.tsx +++ b/src/components/common/Header/SearchBox.tsx @@ -1,32 +1,106 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import Image from 'next/image'; import { SEARCH_ICON } from '@/utils/constants'; +import BASE_URL from '@/constants/BASEURL'; +import useOutsideClick from '@/hooks/useOutsideClick'; import styles from './SearchBox.module.scss'; export default function SearchBox() { const router = useRouter(); - const [input, setInput] = useState(''); + const [keyword, setKeyword] = useState(''); + const [showAutoSuggestion, setShowAutoSuggestion] = useState(false); + const [suggestionList, setSuggestionList] = useState([]); + const autoSuggestionRef = useRef(null); + const inputRef = useRef(null); + useOutsideClick(autoSuggestionRef, () => setShowAutoSuggestion(false)); + + const handleSuggestionClick = (event) => { + setKeyword(event.target.id); + inputRef.current.focus(); + setShowAutoSuggestion(false); + }; + const handleInputChange = (event: React.ChangeEvent) => { - setInput(event.target.value); + setKeyword(event.target.value); }; const handleKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Enter' && input) { - router.push(`/?keyword=${input}`); + if (event.key === 'Enter' && keyword) { + router.push(`/?keyword=${keyword}`); } }; + + useEffect(() => { + let debounce; + if (keyword) { + debounce = setTimeout(async () => { + try { + const response = await fetch( + `${BASE_URL}/notices?limit=10&keyword=${encodeURIComponent(keyword.trim())}&sort=time`, + ); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const searchSet = new Set(); + const data = await response.json(); + data.items.filter((item) => searchSet.add(item.item.shop.item.name)); + setSuggestionList([...searchSet]); + } catch (error) { + console.error('There was a problem with your fetch operation:', error); + } + }, 200); + } else { + setSuggestionList([]); + } + + return () => { + clearTimeout(debounce); + }; + }, [keyword]); + return ( -
e.preventDefault()}> - search + e.preventDefault()} ref={autoSuggestionRef}> + search setShowAutoSuggestion(true)} onChange={handleInputChange} onKeyDown={handleKeyDown} placeholder="가게 이름으로 찾아보세요" /> + {showAutoSuggestion && ( +
+ {suggestionList.map((data) => ( + + ))} + {suggestionList.length === 0 && ( + + )} +
+ )}
); } diff --git a/tsconfig.json b/tsconfig.json index 683cb57..33cad90 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, + "downlevelIteration": true, "plugins": [ { "name": "next"