[검색] 음성검색관련 마이크 노출 주석처리
- 음성 검색우선 제외로 인하여 마이크 버튼주석처리.
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
// 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';
|
||||
@@ -33,7 +39,9 @@ import {
|
||||
// showWarningToast,
|
||||
// } from '../../actions/toastActions';
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
import TItemCardNew, { removeDotAndColon } from '../../components/TItemCard/TItemCard.new';
|
||||
import TItemCardNew, {
|
||||
removeDotAndColon,
|
||||
} from '../../components/TItemCard/TItemCard.new';
|
||||
import TPanel from '../../components/TPanel/TPanel';
|
||||
import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator';
|
||||
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
|
||||
@@ -41,15 +49,22 @@ import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
|
||||
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import { useSearchHistory } from '../../hooks/useSearchHistory';
|
||||
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 { createDebugHelpers } from '../../utils/debug';
|
||||
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 { createDebugHelpers } from '../../utils/debug';
|
||||
import VoiceInputOverlay, {
|
||||
VOICE_MODES,
|
||||
} from './VoiceInputOverlay/VoiceInputOverlay';
|
||||
|
||||
// 디버그 헬퍼 설정
|
||||
const DEBUG_MODE = false;
|
||||
@@ -75,13 +90,22 @@ export const SEARCH_PANEL_MODES = {
|
||||
VOICE_RESULT: 'voice_result', // 음성 검색 결과 표시
|
||||
};
|
||||
|
||||
const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||
const ContainerBasic = SpotlightContainerDecorator(
|
||||
{ enterTo: 'last-focused' },
|
||||
'div'
|
||||
);
|
||||
|
||||
// 검색 입력 영역 컨테이너
|
||||
const InputContainer = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||
const InputContainer = SpotlightContainerDecorator(
|
||||
{ enterTo: 'last-focused' },
|
||||
'div'
|
||||
);
|
||||
|
||||
// 콘텐츠 섹션 컨테이너
|
||||
const SectionContainer = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||
const SectionContainer = SpotlightContainerDecorator(
|
||||
{ enterTo: 'last-focused' },
|
||||
'div'
|
||||
);
|
||||
|
||||
// 메모리 누수 방지를 위한 안전한 이미지 컴포넌트 (컴포넌트 외부로 이동)
|
||||
const SafeImageComponent = ({ src, alt, className, ...props }) => {
|
||||
@@ -118,7 +142,9 @@ const SafeImageComponent = ({ src, alt, className, ...props }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <img ref={imgRef} src={src} alt={alt} className={className} {...props} />;
|
||||
return (
|
||||
<img ref={imgRef} src={src} alt={alt} className={className} {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
const ITEMS_PER_PAGE = 9;
|
||||
@@ -152,22 +178,36 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 0hun: 패널 전역 상태
|
||||
const panels = useSelector((state) => state.panels.panels);
|
||||
// 0hun: 음성 검색 결과에 대한 전역 상태
|
||||
const shopperHouseData = useSelector((state) => state.search.shopperHouseData);
|
||||
const shopperHouseError = useSelector((state) => state.search.shopperHouseError);
|
||||
const shopperHouseData = useSelector(
|
||||
(state) => state.search.shopperHouseData
|
||||
);
|
||||
const shopperHouseError = useSelector(
|
||||
(state) => state.search.shopperHouseError
|
||||
);
|
||||
// 0hun: 음성 검색 searchId (Redux에서 별도 관리)
|
||||
const shopperHouseSearchId = useSelector((state) => state.search.shopperHouseSearchId);
|
||||
const shopperHouseSearchId = useSelector(
|
||||
(state) => state.search.shopperHouseSearchId
|
||||
);
|
||||
// 0hun: 음성 검색 relativeQueries (Redux에서 별도 관리)
|
||||
const shopperHouseRelativeQueries = useSelector(
|
||||
(state) => state.search.shopperHouseRelativeQueries
|
||||
);
|
||||
// 🔄 이전 shopperHouseData (sortingType 변경 시 사용)
|
||||
const preShopperHouseData = useSelector((state) => state.search.preShopperHouseData);
|
||||
const preShopperHouseData = useSelector(
|
||||
(state) => state.search.preShopperHouseData
|
||||
);
|
||||
// 0hun: 검색 메인, Hot Picks for you 영역에 대한 전역 상태 값
|
||||
const hotPicksForYou = useSelector((state) => state.search.searchMainData.hotPicksForYou);
|
||||
const hotPicksForYou = useSelector(
|
||||
(state) => state.search.searchMainData.hotPicksForYou
|
||||
);
|
||||
// 0hun: 검색 메인, Popular Brands 영역에 대한 전역 상태 값
|
||||
const popularBrands = useSelector((state) => state.search.searchMainData.popularBrands);
|
||||
const popularBrands = useSelector(
|
||||
(state) => state.search.searchMainData.popularBrands
|
||||
);
|
||||
// 0hun: 검색 메인, Top Searchs 영역에 대한 전역 상태 값
|
||||
const topSearchs = useSelector((state) => state.search.searchMainData.topSearchs);
|
||||
const topSearchs = useSelector(
|
||||
(state) => state.search.searchMainData.topSearchs
|
||||
);
|
||||
// jhun: 검색 메인, Today Deals 영역에 대한 전역 상태 값
|
||||
const tsvInfo = useSelector((state) => state.search.searchMainData.tsvInfo);
|
||||
|
||||
@@ -177,7 +217,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 0hun: 초기 포커스 유무를 나타내는 Boolean 상태
|
||||
const [firstSpot, setFirstSpot] = useState(false);
|
||||
// 0hun: 검색어 상태
|
||||
const [searchQuery, setSearchQuery] = useState(panelInfo.searchVal ? panelInfo.searchVal : null);
|
||||
const [searchQuery, setSearchQuery] = useState(
|
||||
panelInfo.searchVal ? panelInfo.searchVal : null
|
||||
);
|
||||
// 0hun: 검색 컨테이너 포커스 position 상태 값
|
||||
const [position, setPosition] = useState(null);
|
||||
// 0hun: 가상 키보드 Display 유무 Boolean 값 (주석: 현재 VirtualKeyboardContainer가 비활성화됨)
|
||||
@@ -191,14 +233,17 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// ✨ [Phase 3] TInput의 입력 모드 상태 제거 (더 이상 InputField가 없으므로 불필요)
|
||||
// const [isInputModeActive, setIsInputModeActive] = useState(false);
|
||||
// 0hun: 현재 포커스된 container의 spotlightId를 관리하는 상태 값
|
||||
const [focusedContainerId, setFocusedContainerId] = useState(panelInfo?.focusedContainerId);
|
||||
const [focusedContainerId, setFocusedContainerId] = useState(
|
||||
panelInfo?.focusedContainerId
|
||||
);
|
||||
|
||||
// ✨ [Phase 1] SearchPanel의 현재 모드 상태 (VoiceInputOverlay의 VOICE_MODES와 동일한 개념)
|
||||
const [currentMode, setCurrentMode] = useState(SEARCH_PANEL_MODES.INITIAL);
|
||||
const [isShopperHousePending, setIsShopperHousePending] = useState(false);
|
||||
const [voiceOverlayMode, setVoiceOverlayMode] = useState(VOICE_MODES.PROMPT);
|
||||
const [voiceOverlayResponseText, setVoiceOverlayResponseText] = useState('');
|
||||
const [isVoiceOverlayBubbleSearch, setIsVoiceOverlayBubbleSearch] = useState(false);
|
||||
const [isVoiceOverlayBubbleSearch, setIsVoiceOverlayBubbleSearch] =
|
||||
useState(false);
|
||||
const [shouldFocusVoiceResult, setShouldFocusVoiceResult] = useState(false);
|
||||
|
||||
// 🎯 HowAboutThese 포커스 관리 - 검색 입력 영역 포커스 감지용 상태
|
||||
@@ -285,11 +330,16 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
const unifiedFocusTimerRef = useRef(null);
|
||||
|
||||
// ShopperHouse 에러 팝업 상태 가져오기
|
||||
const shopperHouseErrorPopup = useSelector((state) => state.search.shopperHouseErrorPopup);
|
||||
const shopperHouseErrorPopup = useSelector(
|
||||
(state) => state.search.shopperHouseErrorPopup
|
||||
);
|
||||
|
||||
// API 실패 시 fallback reference 초기화
|
||||
useEffect(() => {
|
||||
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
|
||||
if (
|
||||
shopperHouseErrorPopup?.visible &&
|
||||
shopperHouseErrorPopup?.type === 'API_FAILURE'
|
||||
) {
|
||||
dlog('[SearchPanel] 🧹 API 실패 감지 - fallbackShopperHouseData 초기화');
|
||||
shopperHouseDataRef.current = null;
|
||||
}
|
||||
@@ -307,8 +357,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
/**
|
||||
* useSearchHistory Hook 적용
|
||||
*/
|
||||
const { normalSearches, addNormalSearch, refreshHistory, executeSearchFromHistory } =
|
||||
useSearchHistory();
|
||||
const {
|
||||
normalSearches,
|
||||
addNormalSearch,
|
||||
refreshHistory,
|
||||
executeSearchFromHistory,
|
||||
} = useSearchHistory();
|
||||
|
||||
/**
|
||||
* 🎯 [DetailPanel 복귀 감지] usePanelHistory Hook 적용
|
||||
@@ -422,7 +476,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
// ✨ [Phase 4] Enter/OK 키 처리 - SearchInputOverlay 표시
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
dlog('[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈');
|
||||
dlog(
|
||||
'[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈'
|
||||
);
|
||||
e.preventDefault();
|
||||
|
||||
// ✨ [Phase 6] SearchInputOverlay 오픈 후 자동으로 입력 준비 완료
|
||||
@@ -450,7 +506,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
];
|
||||
if (arrowKeys.includes(e.key)) {
|
||||
// 입력 필드가 비어있고 왼쪽 화살표인 경우에만 방지
|
||||
if (position === 0 && (e.key === 'Left' || e.key === 'ArrowLeft') && !searchQuery) {
|
||||
if (
|
||||
position === 0 &&
|
||||
(e.key === 'Left' || e.key === 'ArrowLeft') &&
|
||||
!searchQuery
|
||||
) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
@@ -461,7 +521,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// DOM 쿼리 최적화: 캐싱된 input element 사용
|
||||
const input =
|
||||
inputElementRef.current ||
|
||||
document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
document.querySelector(
|
||||
`[data-spotlight-id="input-field-box"] > input`
|
||||
);
|
||||
if (input) {
|
||||
inputElementRef.current = input; // 캐싱
|
||||
if (position === input.value.length) {
|
||||
@@ -658,7 +720,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
'[DEBUG]-VOICE_RESULT: Clearing ShopperHouse data (searchId will be preserved for 2nd search)'
|
||||
);
|
||||
dlog('[VoiceInput]-SearchPanel-onCancel-VOICE_RESULT');
|
||||
dlog('[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출');
|
||||
dlog(
|
||||
'[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출'
|
||||
);
|
||||
}
|
||||
// 🎯 [포커스 로직 통합] 포커스는 상태 변경에 의해 자동으로 처리됨
|
||||
setIsShopperHousePending(false);
|
||||
@@ -816,7 +880,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
if (isReturningFromDetailPanel) {
|
||||
const currentSpot = currentPanel?.panelInfo?.currentSpot;
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
||||
dlog(
|
||||
'[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동'
|
||||
);
|
||||
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (usePanelHistory)', {
|
||||
currentSpot,
|
||||
mode: currentMode,
|
||||
@@ -860,7 +926,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
) {
|
||||
const usedHistoryOnTop = currentIsOnTop && isOnTopChange?.becameOnTop;
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
||||
dlog(
|
||||
'[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동'
|
||||
);
|
||||
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (improved fallback)', {
|
||||
currentSpot: panelInfo.currentSpot,
|
||||
mode: currentMode,
|
||||
@@ -928,7 +996,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
prevMode: currentModeRef.current,
|
||||
nextMode: currentMode,
|
||||
isOnTopChanged: isOnTop !== isOnTopRef.current,
|
||||
modeChanged: currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT,
|
||||
modeChanged:
|
||||
currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT,
|
||||
dataChanged: shopperHouseDataRef.current !== shopperHouseData,
|
||||
});
|
||||
}
|
||||
@@ -946,7 +1015,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 이렇게 하면 VOICE_OVERLAY_CLOSED 시나리오에서 TInput으로 가는 것을 방지
|
||||
if (shopperHouseData && currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리');
|
||||
dlog(
|
||||
'[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리'
|
||||
);
|
||||
}
|
||||
return 'NEW_SEARCH_LOADED';
|
||||
}
|
||||
@@ -1009,7 +1080,10 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
let currentSpot = null;
|
||||
|
||||
// 1. usePanelHistory의 currentSpot 우선 사용
|
||||
if (isReturningFromDetailPanel && currentPanel?.panelInfo?.currentSpot) {
|
||||
if (
|
||||
isReturningFromDetailPanel &&
|
||||
currentPanel?.panelInfo?.currentSpot
|
||||
) {
|
||||
currentSpot = currentPanel.panelInfo.currentSpot;
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🎯 usePanelHistory currentSpot 사용:', currentSpot);
|
||||
@@ -1019,13 +1093,19 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
else if (panelInfo?.currentSpot) {
|
||||
currentSpot = panelInfo.currentSpot;
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:', currentSpot);
|
||||
dlog(
|
||||
'[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:',
|
||||
currentSpot
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSpot && currentSpot.startsWith('searchItemContents')) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:', currentSpot);
|
||||
dlog(
|
||||
'[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:',
|
||||
currentSpot
|
||||
);
|
||||
}
|
||||
return currentSpot;
|
||||
} else {
|
||||
@@ -1066,7 +1146,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// SearchInputOverlay에서 검색을 실행하면 isSearchOverlayVisible이 false로 설정되고
|
||||
// 동시에 검색 결과에 따라 모드가 변경되므로, 이 케이스는 검색어 선택 후 닫을 때만 발생
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스');
|
||||
dlog(
|
||||
'[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스'
|
||||
);
|
||||
}
|
||||
return SPOTLIGHT_IDS.SEARCH_INPUT_BOX;
|
||||
|
||||
@@ -1290,17 +1372,27 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
{...rest}
|
||||
>
|
||||
<div className={css.productImageWrapper}>
|
||||
<SafeImage src={bgImgPath} alt={curationNm} className={css.productImage} />
|
||||
<SafeImage
|
||||
src={bgImgPath}
|
||||
alt={curationNm}
|
||||
className={css.productImage}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.productInfo}>
|
||||
{showBrandLogo && (
|
||||
<div className={css.productBrandWrapper}>
|
||||
<SafeImage src={patncLogoPath} alt={patncNm} className={css.brandLogo} />
|
||||
<SafeImage
|
||||
src={patncLogoPath}
|
||||
alt={patncNm}
|
||||
className={css.brandLogo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={css.productDetails}>
|
||||
{showBrandName && <div className={css.brandName}>{patncNm}</div>}
|
||||
{showProductTitle && <div className={css.productTitle}>{curationNm}</div>}
|
||||
{showProductTitle && (
|
||||
<div className={css.productTitle}>{curationNm}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</SpottableProduct>
|
||||
@@ -1323,7 +1415,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
const renderTsvItem = useCallback(
|
||||
({ index, ...rest }) => {
|
||||
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } = tsvInfo[index];
|
||||
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } =
|
||||
tsvInfo[index];
|
||||
|
||||
return (
|
||||
<TItemCardNew
|
||||
@@ -1336,7 +1429,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
priceInfo={priceInfo}
|
||||
productId={prdtId}
|
||||
productName={prdtNm}
|
||||
spotlightId={'searchMain-tsvInfo-spotlightId-' + removeDotAndColon(prdtId)}
|
||||
spotlightId={
|
||||
'searchMain-tsvInfo-spotlightId-' + removeDotAndColon(prdtId)
|
||||
}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
@@ -1603,10 +1698,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 우선순위 2: 음성 검색 결과가 있으면 VOICE_RESULT 모드
|
||||
else if (shopperHouseData || isShopperHousePending) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[DEBUG]-MODE: shopperHouseData EXISTS or pending → VOICE_RESULT', {
|
||||
hasData: !!shopperHouseData,
|
||||
isPending: isShopperHousePending,
|
||||
});
|
||||
dlog(
|
||||
'[DEBUG]-MODE: shopperHouseData EXISTS or pending → VOICE_RESULT',
|
||||
{
|
||||
hasData: !!shopperHouseData,
|
||||
isPending: isShopperHousePending,
|
||||
}
|
||||
);
|
||||
}
|
||||
nextMode = SEARCH_PANEL_MODES.VOICE_RESULT;
|
||||
}
|
||||
@@ -1640,20 +1738,23 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 모드가 변경되었을 때만 업데이트
|
||||
if (nextMode !== currentMode) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog(`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`, {
|
||||
isVoiceOverlayVisible,
|
||||
shopperHouseData: !!shopperHouseData,
|
||||
isShopperHousePending,
|
||||
searchPerformed,
|
||||
searchQuery,
|
||||
hasSearchResults: !!(
|
||||
searchDatas?.theme?.length > 0 ||
|
||||
searchDatas?.item?.length > 0 ||
|
||||
searchDatas?.show?.length > 0
|
||||
),
|
||||
isSearchOverlayVisible,
|
||||
inputFocus,
|
||||
});
|
||||
dlog(
|
||||
`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`,
|
||||
{
|
||||
isVoiceOverlayVisible,
|
||||
shopperHouseData: !!shopperHouseData,
|
||||
isShopperHousePending,
|
||||
searchPerformed,
|
||||
searchQuery,
|
||||
hasSearchResults: !!(
|
||||
searchDatas?.theme?.length > 0 ||
|
||||
searchDatas?.item?.length > 0 ||
|
||||
searchDatas?.show?.length > 0
|
||||
),
|
||||
isSearchOverlayVisible,
|
||||
inputFocus,
|
||||
}
|
||||
);
|
||||
}
|
||||
setCurrentMode(nextMode);
|
||||
} else {
|
||||
@@ -1779,10 +1880,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// NEW_SEARCH_LOADED: 음성 검색 결과 로드 시 VoiceInputOverlay와 충돌 방지
|
||||
// 다른 시나리오에서는 기존과 같은 지연 시간 (100ms)
|
||||
const focusDelay =
|
||||
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED' ? 50 : 100;
|
||||
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED'
|
||||
? 50
|
||||
: 100;
|
||||
|
||||
unifiedFocusTimerRef.current = setTimeout(() => {
|
||||
const targetElement = document.querySelector(`[data-spotlight-id="${targetId}"]`);
|
||||
const targetElement = document.querySelector(
|
||||
`[data-spotlight-id="${targetId}"]`
|
||||
);
|
||||
|
||||
if (targetElement || targetId === SPOTLIGHT_IDS.SEARCH_INPUT_BOX) {
|
||||
Spotlight.focus(targetId);
|
||||
@@ -1805,9 +1910,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}
|
||||
|
||||
// 🎯 DETAIL_PANEL_RETURN에서 요소를 찾지 못하면 fallback으로 첫 번째 상품 시도
|
||||
if (scenario === 'DETAIL_PANEL_RETURN' && targetId.startsWith('searchItemContents')) {
|
||||
if (
|
||||
scenario === 'DETAIL_PANEL_RETURN' &&
|
||||
targetId.startsWith('searchItemContents')
|
||||
) {
|
||||
const fallbackTarget = 'searchItemContents0';
|
||||
const fallbackElement = document.querySelector(`[data-spotlight-id="${fallbackTarget}"]`);
|
||||
const fallbackElement = document.querySelector(
|
||||
`[data-spotlight-id="${fallbackTarget}"]`
|
||||
);
|
||||
if (fallbackElement) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog(
|
||||
@@ -1825,10 +1935,15 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
// 🎯 [NEW_SEARCH_LOADED] 1초 후 다시 첫 번째 아이템으로 포커스 이동
|
||||
// TInputSimple과 Mic Icon의 포커스 충돌 해결을 위해
|
||||
if (scenario === 'NEW_SEARCH_LOADED' && targetId === 'searchItemContents0') {
|
||||
if (
|
||||
scenario === 'NEW_SEARCH_LOADED' &&
|
||||
targetId === 'searchItemContents0'
|
||||
) {
|
||||
setTimeout(() => {
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🔄 NEW_SEARCH_LOADED: 1초 후 첫 번째 상품으로 다시 포커스 이동');
|
||||
dlog(
|
||||
'[FOCUS] 🔄 NEW_SEARCH_LOADED: 1초 후 첫 번째 상품으로 다시 포커스 이동'
|
||||
);
|
||||
}
|
||||
Spotlight.focus('searchItemContents0');
|
||||
}, 500); // 0.5초 후
|
||||
@@ -1866,7 +1981,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
isVoiceOverlayVisibleRef.current = isVoiceOverlayVisible;
|
||||
isSearchOverlayVisibleRef.current = isSearchOverlayVisible;
|
||||
currentModeRef.current = currentMode;
|
||||
}, [shopperHouseData, searchDatas, isVoiceOverlayVisible, isSearchOverlayVisible, currentMode]);
|
||||
}, [
|
||||
shopperHouseData,
|
||||
searchDatas,
|
||||
isVoiceOverlayVisible,
|
||||
isSearchOverlayVisible,
|
||||
currentMode,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 🎯 SearchInputOverlay 닫힘 후 포커스 관리
|
||||
@@ -1889,10 +2010,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
});
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🎯 SearchInputOverlay 닫힘 후 포커스 관리 useEffect 실행', {
|
||||
shouldFocusSearchInput,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
dlog(
|
||||
'[FOCUS] 🎯 SearchInputOverlay 닫힘 후 포커스 관리 useEffect 실행',
|
||||
{
|
||||
shouldFocusSearchInput,
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 500ms 후 TInputSimple에 포커스 이동
|
||||
@@ -1903,10 +2027,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
});
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] ⏰ 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동', {
|
||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
dlog(
|
||||
'[FOCUS] ⏰ 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동',
|
||||
{
|
||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
dlog('[DEBUG] Spotlight.focus() 호출 직전', {
|
||||
@@ -1950,9 +2077,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
});
|
||||
if (focusTimer) {
|
||||
if (DEBUG_MODE) {
|
||||
dlog('[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리', {
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
dlog(
|
||||
'[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리',
|
||||
{
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
clearTimeout(focusTimer);
|
||||
}
|
||||
@@ -2053,12 +2183,18 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}, [currentMode, isOnTop, refreshHistory]);
|
||||
|
||||
return (
|
||||
<TPanel className={css.container} handleCancel={onCancel} spotlightId={spotlightId}>
|
||||
<TPanel
|
||||
className={css.container}
|
||||
handleCancel={onCancel}
|
||||
spotlightId={spotlightId}
|
||||
>
|
||||
{/* ✨ [Phase 2] spotlightDisabled를 currentMode로 제어 */}
|
||||
<TBody
|
||||
className={css.tBody}
|
||||
scrollable
|
||||
spotlightDisabled={!isOnTop || currentMode === SEARCH_PANEL_MODES.SEARCH_INPUT}
|
||||
spotlightDisabled={
|
||||
!isOnTop || currentMode === SEARCH_PANEL_MODES.SEARCH_INPUT
|
||||
}
|
||||
>
|
||||
<ContainerBasic>
|
||||
{isOnTop && (
|
||||
@@ -2078,7 +2214,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
className={classNames(
|
||||
css.inputContainer,
|
||||
inputFocus === true && css.inputFocus,
|
||||
searchDatas && css.searchValue /* 이건 결과값 있을때만. 조건 추가필요 */,
|
||||
searchDatas &&
|
||||
css.searchValue /* 이건 결과값 있을때만. 조건 추가필요 */,
|
||||
(currentMode === SEARCH_PANEL_MODES.VOICE_INPUT ||
|
||||
currentMode === SEARCH_PANEL_MODES.INPUT_FOCUSED) &&
|
||||
css.hidden
|
||||
@@ -2129,7 +2266,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
placeholder="Search products or brands"
|
||||
/>
|
||||
</div>
|
||||
<SpottableMicButton
|
||||
{/* <SpottableMicButton
|
||||
className={css.microphoneButton}
|
||||
onClick={onClickMic}
|
||||
onFocus={onFocusMic}
|
||||
@@ -2148,7 +2285,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
<div className={css.microphoneCircle}>
|
||||
<SafeImage src={micIcon} alt="Microphone" className={css.microphoneIcon} />
|
||||
</div>
|
||||
</SpottableMicButton>
|
||||
</SpottableMicButton> */}
|
||||
</div>
|
||||
</InputContainer>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user