Skip to content

Commit 6febd6b

Browse files
authored
[FE] [Fix, Feature] 최근검색어 삭제기능, hover 개선 (#128)
1 parent b969cda commit 6febd6b

File tree

5 files changed

+55
-15
lines changed

5 files changed

+55
-15
lines changed

frontend/src/components/IconButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import styled from 'styled-components';
33
interface IProps {
44
icon: JSX.Element;
55
onClick?: React.MouseEventHandler;
6+
onMouseDown?: React.MouseEventHandler;
67
'aria-label': string;
78
}
89

9-
const IconButton = ({ icon, onClick, ...rest }: IProps) => {
10+
const IconButton = ({ icon, ...rest }: IProps) => {
1011
return (
11-
<Button type="button" onClick={onClick} {...rest}>
12+
<Button type="button" {...rest}>
1213
{icon}
1314
</Button>
1415
);

frontend/src/components/search/RecentKeywordsList.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { ClockIcon } from '@/icons';
1+
import { IconButton } from '@/components';
2+
import { ClockIcon, XIcon } from '@/icons';
3+
import { setLocalStorage } from '@/utils/storage';
24
import { isEmpty } from 'lodash-es';
35
import { Dispatch, SetStateAction, useEffect } from 'react';
46
import styled from 'styled-components';
@@ -8,14 +10,23 @@ interface RecentKeywordsListProps {
810
hoverdIndex: number;
911
handleMouseDown: (prop: string) => void;
1012
setHoveredIndex: Dispatch<SetStateAction<number>>;
13+
initializeRecentKeywords: () => void;
1114
}
1215

1316
const RecentKeywordsList = ({
1417
recentKeywords,
1518
hoverdIndex,
1619
handleMouseDown,
1720
setHoveredIndex,
21+
initializeRecentKeywords,
1822
}: RecentKeywordsListProps) => {
23+
const handleRecentKeywordRemove = (e: React.MouseEvent, keyword: string) => {
24+
e.preventDefault();
25+
e.stopPropagation();
26+
setLocalStorage('recentKeywords', Array.from([...recentKeywords.filter((v) => v !== keyword)]));
27+
initializeRecentKeywords();
28+
};
29+
1930
useEffect(() => {
2031
setHoveredIndex(-1);
2132
}, [setHoveredIndex]);
@@ -32,6 +43,11 @@ const RecentKeywordsList = ({
3243
>
3344
<ClockIcon />
3445
{keyword}
46+
<DeleteButton
47+
icon={<XIcon />}
48+
onMouseDown={(e) => handleRecentKeywordRemove(e, keyword)}
49+
aria-label="삭제"
50+
/>
3551
</Keyword>
3652
))
3753
) : (
@@ -61,4 +77,17 @@ const NoResult = styled.div`
6177
overflow: hidden;
6278
`;
6379

80+
const DeleteButton = styled(IconButton)`
81+
margin-left: auto;
82+
width: 20px;
83+
height: 20px;
84+
display: flex;
85+
justify-content: center;
86+
align-items: center;
87+
:hover {
88+
background-color: ${({ theme }) => theme.COLOR.offWhite};
89+
border-radius: 50%;
90+
}
91+
`;
92+
6493
export default RecentKeywordsList;

frontend/src/components/search/Search.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ const Search = ({ initialKeyword = '' }: SearchProps) => {
6565
[navigate],
6666
);
6767

68-
const getRecentKeywordsFromLocalStorage = useCallback(() => {
68+
const getRecentKeywords = () => {
6969
const result = getLocalStorage('recentKeywords');
7070
if (!Array.isArray(result)) {
7171
return [];
7272
}
7373
return result;
74-
}, []);
74+
};
7575

7676
// 논문 상세정보 페이지로 이동
7777
const goToDetailPage = (doi: string, state?: { initialData: IPaperDetail }) => {
@@ -85,24 +85,26 @@ const Search = ({ initialKeyword = '' }: SearchProps) => {
8585

8686
// localStorage에서 가져온 recent keywords를 최근에 검색한 순서대로 set
8787
const handleInputFocus = () => {
88-
const recentKeywords = getRecentKeywordsFromLocalStorage();
89-
setRecentKeywords(recentKeywords.reverse());
9088
setIsFocused(true);
9189
};
9290

9391
const handleInputBlur = () => {
9492
setIsFocused(false);
9593
};
9694

95+
const initializeRecentKeywords = useCallback(() => {
96+
const recentKeywords = getRecentKeywords();
97+
setRecentKeywords(recentKeywords);
98+
}, []);
99+
97100
// localStorage에 최근 검색어를 중복없이 최대 5개까지 저장 후 search-list로 이동
98101
const handleSearchButtonClick = async (newKeyword: string) => {
99102
if (!newKeyword) return;
100103
setKeyword(newKeyword);
101-
const recentKeywords = getRecentKeywordsFromLocalStorage();
102-
const recentSet = new Set(recentKeywords);
103-
recentSet.delete(newKeyword);
104-
recentSet.add(newKeyword);
105-
setLocalStorage('recentKeywords', Array.from(recentSet).slice(-5));
104+
setLocalStorage(
105+
'recentKeywords',
106+
Array.from([newKeyword, ...recentKeywords.filter((keyword) => keyword !== newKeyword)]).slice(0, 5),
107+
);
106108

107109
// DOI 형식의 input이 들어온 경우
108110
if (isDoiFormat(newKeyword)) {
@@ -139,10 +141,10 @@ const Search = ({ initialKeyword = '' }: SearchProps) => {
139141

140142
switch (e.code) {
141143
case 'ArrowDown':
142-
setHoveredIndex((prev) => (prev + 1) % length);
144+
setHoveredIndex((prev) => (prev + 1 > length - 1 ? -1 : prev + 1));
143145
break;
144146
case 'ArrowUp':
145-
setHoveredIndex((prev) => (prev - 1 < 0 ? length - 1 : (prev - 1) % length));
147+
setHoveredIndex((prev) => (prev - 1 < -1 ? length - 1 : prev - 1));
146148
break;
147149
case 'Enter':
148150
handleEnterKeyDown();
@@ -167,6 +169,7 @@ const Search = ({ initialKeyword = '' }: SearchProps) => {
167169
hoverdIndex={hoverdIndex}
168170
handleMouseDown={handleSearchButtonClick}
169171
setHoveredIndex={setHoveredIndex}
172+
initializeRecentKeywords={initializeRecentKeywords}
170173
/>
171174
),
172175
[DROPDOWN_TYPE.LOADING]: <MoonLoader />,
@@ -178,6 +181,10 @@ const Search = ({ initialKeyword = '' }: SearchProps) => {
178181
setKeyword(initialKeyword);
179182
}, [initialKeyword]);
180183

184+
useEffect(() => {
185+
initializeRecentKeywords();
186+
}, [initializeRecentKeywords]);
187+
181188
return (
182189
<Container>
183190
<SearchBox>

frontend/src/icons/XIcon.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import theme from '@/style/theme';
2+
13
const XIcon = () => {
24
return (
3-
<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="15" height="15" viewBox="0 0 320 512">
5+
<svg xmlns="http://www.w3.org/2000/svg" fill={theme.COLOR.gray3} width="15" height="15" viewBox="0 0 320 512">
46
<path d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z" />
57
</svg>
68
);

frontend/src/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export { default as GithubLogoIcon } from './GithubLogoIcon';
55
export { default as LogoIcon } from './LogoIcon';
66
export { default as MagnifyingGlassIcon } from './MagnifyingGlassIcon';
77
export { default as PreviousButtonIcon } from './PreviousButtonIcon';
8+
export { default as XIcon } from './XIcon';

0 commit comments

Comments
 (0)