-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat: SearchBar 컴포넌트 구현 * feat: Search 페이지 구현 * feat: 홈페이지에 searchBar 적용 * refactor: 불필요한 memo 제거
- Loading branch information
Showing
6 changed files
with
223 additions
and
2 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { useState } from 'react'; | ||
import styled from 'styled-components'; | ||
import useNavigator from '../../hooks/useNavigator'; | ||
import SearchIcon from '../../assets/search.svg'; | ||
|
||
const SearchBar = () => { | ||
const { routingHandlers } = useNavigator(); | ||
|
||
const [searchTerm, setSearchTerm] = useState(''); | ||
|
||
const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setSearchTerm(e.target.value); | ||
}; | ||
|
||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
routingHandlers.search(searchTerm); | ||
}; | ||
|
||
return ( | ||
<SearchBarWrapper onSubmit={onSubmit}> | ||
<StyledSearchIcon /> | ||
<SearchInput | ||
type="text" | ||
placeholder="관심있는 키워드를 입력하세요" | ||
onChange={onInputChange} | ||
/> | ||
</SearchBarWrapper> | ||
); | ||
}; | ||
export default SearchBar; | ||
|
||
const SearchBarWrapper = styled.form` | ||
display: flex; | ||
padding-left: 20px; | ||
position: relative; | ||
border-radius: 5px; | ||
border: 1px solid #ccc; | ||
box-shadow: 0px 1px 5px 3px rgba(0, 0, 0, 0.12); | ||
`; | ||
|
||
const SearchInput = styled.input` | ||
height: 45px; | ||
width: 100%; | ||
outline: none; | ||
border: none; | ||
border-radius: 5px; | ||
padding: 0 60px 0 20px; | ||
font-size: 18px; | ||
font-weight: 500; | ||
&:focus { | ||
outline: none !important; | ||
box-shadow: | ||
inset -1px -1px rgba(0, 0, 0, 0.075), | ||
inset -1px -1px rgba(0, 0, 0, 0.075), | ||
inset -3px -3px rgba(255, 255, 255, 0.6), | ||
inset -4px -4px rgba(255, 255, 255, 0.6), | ||
inset rgba(255, 255, 255, 0.6), | ||
inset rgba(64, 64, 64, 0.15); | ||
} | ||
`; | ||
|
||
const StyledSearchIcon = styled(SearchIcon)` | ||
position: absolute; | ||
top: 50%; | ||
left: 10px; | ||
transform: translateY(-50%); | ||
width: 20px; | ||
height: 20px; | ||
fill: #ccc; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import styled from 'styled-components'; | ||
import { setFullScreenResponsive } from '../constants/responsive'; | ||
import Space from '../components/common/Space'; | ||
import SearchBar from '../components/SearchBar/SearchBar'; | ||
import { Fragment, useEffect, useState } from 'react'; | ||
import Flex from '../components/common/Flex'; | ||
import Box from '../components/common/Box'; | ||
import Text from '../components/common/Text'; | ||
import TopicCard from '../components/TopicCard'; | ||
import { TopicCardProps } from '../types/Topic'; | ||
import { useLocation } from 'react-router-dom'; | ||
import useGet from '../apiHooks/useGet'; | ||
|
||
const Search = () => { | ||
const { fetchGet } = useGet(); | ||
|
||
const [originalTopics, setOriginalTopics] = useState<TopicCardProps[] | null>( | ||
null, | ||
); | ||
const [displayedTopics, setDisplayedTopics] = useState< | ||
TopicCardProps[] | null | ||
>(null); | ||
const searchQuery = decodeURIComponent(useLocation().search.substring(1)); | ||
|
||
const getTopicsFromServer = async () => { | ||
fetchGet<TopicCardProps[]>( | ||
'/topics', | ||
'지도를 가져오는데 실패했습니다.', | ||
(response) => { | ||
setOriginalTopics(response); | ||
const searchResult = response.filter((topic) => | ||
topic.name.includes(searchQuery), | ||
); | ||
setDisplayedTopics(searchResult); | ||
}, | ||
); | ||
}; | ||
|
||
useEffect(() => { | ||
getTopicsFromServer(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (originalTopics) { | ||
const searchResult = originalTopics.filter((topic) => | ||
topic.name.includes(searchQuery), | ||
); | ||
setDisplayedTopics(searchResult); | ||
} | ||
}, [searchQuery]); | ||
|
||
return ( | ||
<Wrapper> | ||
<Space size={1} /> | ||
<SearchBar /> | ||
<Space size={1} /> | ||
<Flex $justifyContent="space-between" $alignItems="flex-end"> | ||
<Box> | ||
<Text | ||
color="black" | ||
$fontSize="extraLarge" | ||
$fontWeight="bold" | ||
tabIndex={0} | ||
> | ||
찾았을 지도? | ||
</Text> | ||
<Space size={0} /> | ||
<Text | ||
color="gray" | ||
$fontSize="default" | ||
$fontWeight="normal" | ||
tabIndex={1} | ||
> | ||
검색한 지도를 확인해보세요. | ||
</Text> | ||
</Box> | ||
</Flex> | ||
<Space size={6} /> | ||
|
||
{displayedTopics?.length === 0 ? ( | ||
// 검색 결과가 없을 때의 UI | ||
<EmptyWrapper> | ||
<Flex $alignItems="center"> | ||
<Space size={1} /> | ||
<Text color="black" $fontSize="default" $fontWeight="normal"> | ||
'{searchQuery}'에 대한 | ||
{'검색 결과가 없습니다.'} | ||
</Text> | ||
<Space size={4} /> | ||
</Flex> | ||
<Space size={5} /> | ||
</EmptyWrapper> | ||
) : ( | ||
<CardListWrapper> | ||
{displayedTopics?.map((topic) => ( | ||
<Fragment key={topic.id}> | ||
<TopicCard | ||
cardType="default" | ||
id={topic.id} | ||
image={topic.image} | ||
name={topic.name} | ||
creator={topic.creator} | ||
updatedAt={topic.updatedAt} | ||
pinCount={topic.pinCount} | ||
bookmarkCount={topic.bookmarkCount} | ||
isInAtlas={topic.isInAtlas} | ||
isBookmarked={topic.isBookmarked} | ||
/> | ||
</Fragment> | ||
))} | ||
</CardListWrapper> | ||
)} | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
export default Search; | ||
|
||
const Wrapper = styled.article` | ||
width: 1036px; | ||
margin: 0 auto; | ||
position: relative; | ||
${setFullScreenResponsive()} | ||
`; | ||
|
||
const CardListWrapper = styled.ul` | ||
display: flex; | ||
flex-wrap: wrap; | ||
gap: 20px; | ||
`; | ||
|
||
const EmptyWrapper = styled.section` | ||
height: 240px; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters