[서치] 최근검색어 삭제 및 스타일 변경

- removeNormalSearch 추가
 - searchinputoverlay에 제거 관련 함수 추가
 - searchpanel에 모드 변경시 refreshHistory추가.
This commit is contained in:
junghoon86.park
2025-10-29 15:14:28 +09:00
parent b880817115
commit 09a7be910d
4 changed files with 106 additions and 25 deletions

View File

@@ -1,14 +1,23 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useDispatch } from 'react-redux';
import { getSearch, getShopperHouseSearch } from '../actions/searchActions';
import {
readSearchHistory,
writeSearchHistory,
getSearch,
getShopperHouseSearch,
} from '../actions/searchActions';
import {
addSearchHistory,
removeSearchHistory,
clearAllSearchHistory,
migrateVoiceSearchHistory,
readSearchHistory,
removeSearchHistory,
writeSearchHistory,
} from '../utils/searchHistory';
export const useSearchHistory = () => {
@@ -23,7 +32,7 @@ export const useSearchHistory = () => {
const normalSearches = useMemo(() => {
return searchHistory
.filter(item => item.type === 'normal')
.slice(0, 8); // SearchPanel에서 최대 8개 표시
.slice(0, 5); // SearchPanel에서 최대 5개 표시
}, [searchHistory]);
// 음성 검색 기록 필터링
@@ -49,6 +58,26 @@ export const useSearchHistory = () => {
setSearchHistory(newHistory);
}, []);
// 일반 검색 기록 삭제
const removeNormalSearch = useCallback((query) => {
if (!query || typeof query !== 'string') {
console.warn('[useSearchHistory] Invalid query for removal');
return;
}
const currentHistory = readSearchHistory();
const itemToRemove = currentHistory.find(
(item) => item.query === query.trim() && item.type === 'normal'
);
if (itemToRemove) {
const newHistory = removeSearchHistory(itemToRemove.timestamp);
setSearchHistory(newHistory);
} else {
console.warn(`[useSearchHistory] Normal search not found: "${query}"`);
}
}, []);
// 음성 검색 기록 추가
const addVoiceSearch = useCallback((query, searchId = null) => {
if (!query || typeof query !== 'string' || !query.trim()) {
@@ -125,6 +154,7 @@ export const useSearchHistory = () => {
// 액션 함수
addNormalSearch,
removeNormalSearch,
addVoiceSearch,
executeSearchFromHistory,
removeHistoryItem,

View File

@@ -41,7 +41,8 @@ const SearchInputOverlay = ({
handleClick,
}) => {
const dispatch = useDispatch();
const { addNormalSearch, normalSearches } = useSearchHistory();
const { addNormalSearch, removeNormalSearch, normalSearches } =
useSearchHistory();
// ✨ [Phase 3] Input 포커스 상태에 따른 placeholder 동적 변경
const [inputFocused, setInputFocused] = useState(false);
@@ -99,6 +100,12 @@ const SearchInputOverlay = ({
[dispatch, addNormalSearch, onClose, onSearchChange]
);
const handleRemoveSearch = (query) => {
if (query && query.trim()) {
removeNormalSearch(query.trim());
}
};
const handleKeydown = useCallback(
(e) => {
// Enter 키 또는 OK 키 처리
@@ -220,11 +227,18 @@ const SearchInputOverlay = ({
// 검색어 설정 후 검색 실행
handleClick(keyword);
}}
tabIndex={0}
spotlightId={`recent-Resultkeyword-${index}`}
>
{keyword}
</SpottableKeyword>
<SpottableKeyword className={css.closeBtn}>
<SpottableKeyword
className={css.closeBtn}
onClick={() => {
handleRemoveSearch(keyword);
}}
tabIndex={0}
>
<img src={closeImg} className={css.closeBtnImg} />
</SpottableKeyword>
</div>

View File

@@ -32,7 +32,7 @@
width: 100%;
height: 100%;
z-index: 1002;
pointer-events: none; // 빈 공간 클릭 시 dimBackground로 이벤트 전달
// pointer-events: none; // 빈 공간 클릭 시 dimBackground로 이벤트 전달
display: flex;
flex-direction: column;
align-items: center;

View File

@@ -1,26 +1,44 @@
// src/views/SearchPanel/SearchPanel.new.jsx
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import {
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
import hotPicksBrandImage from '../../../assets/images/searchpanel/img-search-hotpicks.png';
import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions';
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
import { popPanel, pushPanel, updatePanel } from '../../actions/panelActions';
import hotPicksBrandImage
from '../../../assets/images/searchpanel/img-search-hotpicks.png';
import {
sendLogGNB,
sendLogTotalRecommend,
} from '../../actions/logActions';
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
import {
popPanel,
pushPanel,
updatePanel,
} from '../../actions/panelActions';
import {
clearShopperHouseData,
getSearch,
getSearchMain,
getShopperHouseSearch,
resetSearch,
resetVoiceSearch,
clearShopperHouseData,
getShopperHouseSearch,
} from '../../actions/searchActions';
// import {
// showErrorToast,
@@ -32,20 +50,32 @@ import {
// } from '../../actions/toastActions';
import TBody from '../../components/TBody/TBody';
import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator';
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator';
import TVirtualGridList
from '../../components/TVirtualGridList/TVirtualGridList';
import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
import usePrevious from '../../hooks/usePrevious';
import { useSearchHistory } from '../../hooks/useSearchHistory';
import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../utils/Config';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from '../../utils/Config';
import NoSearchResults from './NoSearchResults/NoSearchResults';
// import NoSearchResults from './NoSearchResults/NoSearchResults';
import SearchInputOverlay from './SearchInputOverlay';
import css from './SearchPanel.new.module.less';
import SearchResultsNew from './SearchResults.new.v2';
import TInputSimple, { ICONS, KINDS } from './TInput/TInputSimple';
import VoiceInputOverlay, { VOICE_MODES } from './VoiceInputOverlay/VoiceInputOverlay';
import TInputSimple, {
ICONS,
KINDS,
} from './TInput/TInputSimple';
import VoiceInputOverlay, {
VOICE_MODES,
} from './VoiceInputOverlay/VoiceInputOverlay';
/**
* ✨ Mode-Based Architecture 도입
@@ -277,7 +307,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
/**
* useSearchHistory Hook 적용
*/
const { normalSearches, addNormalSearch, executeSearchFromHistory } = useSearchHistory();
const { normalSearches, addNormalSearch, refreshHistory, executeSearchFromHistory } = useSearchHistory();
/**
* 🎯 [DetailPanel 복귀 감지] usePanelHistory Hook 적용
@@ -1738,6 +1768,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//모드 변경시 최근 검색어 새로고침.
useEffect(() => {
if (currentMode === SEARCH_PANEL_MODES.INITIAL && isOnTop) {
refreshHistory();
}
}, [currentMode, isOnTop, refreshHistory]);
return (
<TPanel className={css.container} handleCancel={onCancel} spotlightId={spotlightId}>
{/* ✨ [Phase 2] spotlightDisabled를 currentMode로 제어 */}