[검색] 음성검색관련 마이크 노출 주석처리

- 음성 검색우선 제외로 인하여 마이크 버튼주석처리.
This commit is contained in:
junghoon86.park
2025-12-15 16:23:54 +09:00
parent c540378cb5
commit 802484debd

View File

@@ -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>