[서치] 최근검색어 삭제 및 스타일 변경
- removeNormalSearch 추가 - searchinputoverlay에 제거 관련 함수 추가 - searchpanel에 모드 변경시 refreshHistory추가.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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로 제어 */}
|
||||
|
||||
Reference in New Issue
Block a user