[251026] fix: App.js 전역 SpotlightFocus FOCUS_DEBUG에 따라서 로그기능
🕐 커밋 시간: 2025. 10. 26. 14:59:46 📊 변경 통계: • 총 파일: 4개 • 추가: +191줄 • 삭제: -7줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/App/App.js ~ com.twin.app.shoptime/src/utils/Config.js ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.jsx 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • 공통 유틸리티 함수 최적화 • 중간 규모 기능 개선
This commit is contained in:
@@ -267,10 +267,62 @@ console.warn = function (...args) {
|
||||
};
|
||||
|
||||
const originFocus = Spotlight.focus;
|
||||
const originMove = Spotlight.move;
|
||||
const originSilentlyFocus = Spotlight.silentlyFocus;
|
||||
let lastLoggedSpotlightId = null;
|
||||
let lastLoggedBlurSpotlightId = null;
|
||||
let focusLoggingSuppressed = 0;
|
||||
|
||||
const resolveSpotlightIdFromNode = (node) => {
|
||||
if (!node) return undefined;
|
||||
|
||||
if (node.dataset && node.dataset.spotlightId) {
|
||||
return node.dataset.spotlightId;
|
||||
}
|
||||
|
||||
if (typeof node.getAttribute === 'function') {
|
||||
const idFromAttr = node.getAttribute('data-spotlight-id');
|
||||
if (idFromAttr) {
|
||||
return idFromAttr;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.id) {
|
||||
return node.id;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const logFocusTransition = (previousNode, currentNode) => {
|
||||
if (!Config.FOCUS_DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousId = resolveSpotlightIdFromNode(previousNode);
|
||||
const currentId = resolveSpotlightIdFromNode(currentNode);
|
||||
|
||||
if (previousId && previousId !== currentId) {
|
||||
console.log(`[SpotlightFocus] blur - ${previousId}`);
|
||||
lastLoggedBlurSpotlightId = previousId;
|
||||
}
|
||||
|
||||
if (currentId && currentId !== lastLoggedSpotlightId) {
|
||||
console.log(`[SpotlightFocus] focus - ${currentId}`);
|
||||
lastLoggedSpotlightId = currentId;
|
||||
}
|
||||
};
|
||||
|
||||
Spotlight.focus = function (elem, containerOption) {
|
||||
const previousNode = Spotlight.getCurrent();
|
||||
|
||||
const ret = originFocus.apply(this, [elem, containerOption]); // this 바인딩을 유지하여 originFocus 호출
|
||||
if (ret === true) {
|
||||
const current = Spotlight.getCurrent();
|
||||
if ((ret === true || current !== previousNode) && focusLoggingSuppressed === 0) {
|
||||
logFocusTransition(previousNode, current);
|
||||
}
|
||||
|
||||
if (ret === true) {
|
||||
const floatLayerNode = document.getElementById('floatLayer');
|
||||
const tabLayoutNode = document.getElementById(SpotlightIds.TAB_LAYOUT);
|
||||
//팝업이 존재할 경우
|
||||
@@ -300,6 +352,74 @@ Spotlight.focus = function (elem, containerOption) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
Spotlight.move = function (...args) {
|
||||
if (!originMove) {
|
||||
return false;
|
||||
}
|
||||
const previousNode = Spotlight.getCurrent();
|
||||
focusLoggingSuppressed += 1;
|
||||
let ret;
|
||||
try {
|
||||
ret = originMove.apply(this, args);
|
||||
} finally {
|
||||
focusLoggingSuppressed = Math.max(0, focusLoggingSuppressed - 1);
|
||||
}
|
||||
|
||||
const current = Spotlight.getCurrent();
|
||||
|
||||
if (current !== previousNode) {
|
||||
logFocusTransition(previousNode, current);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Spotlight.silentlyFocus = function (...args) {
|
||||
if (!originSilentlyFocus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const previousNode = Spotlight.getCurrent();
|
||||
focusLoggingSuppressed += 1;
|
||||
let ret;
|
||||
try {
|
||||
ret = originSilentlyFocus.apply(this, args);
|
||||
} finally {
|
||||
focusLoggingSuppressed = Math.max(0, focusLoggingSuppressed - 1);
|
||||
}
|
||||
|
||||
const current = Spotlight.getCurrent();
|
||||
|
||||
if (current !== previousNode) {
|
||||
logFocusTransition(previousNode, current);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
const resolveSpotlightIdFromEvent = (event) => {
|
||||
if (!event) return undefined;
|
||||
const { detail, target } = event;
|
||||
|
||||
if (detail) {
|
||||
if (detail.spotlightId) {
|
||||
return detail.spotlightId;
|
||||
}
|
||||
if (detail.id) {
|
||||
return detail.id;
|
||||
}
|
||||
if (detail.target && detail.target.dataset && detail.target.dataset.spotlightId) {
|
||||
return detail.target.dataset.spotlightId;
|
||||
}
|
||||
}
|
||||
|
||||
if (target && target.dataset && target.dataset.spotlightId) {
|
||||
return target.dataset.spotlightId;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
function AppBase(props) {
|
||||
const dispatch = useDispatch();
|
||||
const httpHeader = useSelector((state) => state.common.httpHeader);
|
||||
@@ -317,6 +437,53 @@ function AppBase(props) {
|
||||
// const termsFlag = useSelector((state) => state.common.termsFlag);
|
||||
const termsData = useSelector((state) => state.home.termsData);
|
||||
|
||||
useEffect(() => {
|
||||
if (!Config.FOCUS_DEBUG) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const handleFocusLog = (event) => {
|
||||
const spotlightId = resolveSpotlightIdFromEvent(event);
|
||||
if (!spotlightId || spotlightId === lastLoggedSpotlightId) {
|
||||
return;
|
||||
}
|
||||
console.log(`[SpotlightFocus] focus - ${spotlightId}`);
|
||||
lastLoggedSpotlightId = spotlightId;
|
||||
};
|
||||
|
||||
const handleBlurLog = (event) => {
|
||||
const spotlightId = resolveSpotlightIdFromEvent(event);
|
||||
if (!spotlightId || spotlightId === lastLoggedBlurSpotlightId) {
|
||||
return;
|
||||
}
|
||||
console.log(`[SpotlightFocus] blur - ${spotlightId}`);
|
||||
lastLoggedBlurSpotlightId = spotlightId;
|
||||
};
|
||||
|
||||
const hasSpotlightListener = typeof Spotlight.addEventListener === 'function';
|
||||
if (hasSpotlightListener) {
|
||||
Spotlight.addEventListener('focus', handleFocusLog);
|
||||
Spotlight.addEventListener('blur', handleBlurLog);
|
||||
|
||||
return () => {
|
||||
Spotlight.removeEventListener('focus', handleFocusLog);
|
||||
Spotlight.removeEventListener('blur', handleBlurLog);
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('spotlightfocus', handleFocusLog);
|
||||
document.addEventListener('spotlightblur', handleBlurLog);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('spotlightfocus', handleFocusLog);
|
||||
document.removeEventListener('spotlightblur', handleBlurLog);
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [Config.FOCUS_DEBUG]);
|
||||
|
||||
useEffect(() => {
|
||||
// Chromium68 호환성을 위해 Optional Chaining 제거
|
||||
if (termsData && termsData.data && termsData.data.terms) {
|
||||
|
||||
@@ -616,3 +616,6 @@ export const LOG_MESSAGE_ID = {
|
||||
CHECKOUT_CLICK: 'AL_CHECKOUT_CLICK',
|
||||
BUY_NOW: 'AL_BUY_NOW',
|
||||
};
|
||||
|
||||
// Spotlight 포커스 디버깅 플래그 (true 시 포커스 이동 로그 활성화)
|
||||
export const FOCUS_DEBUG = true;
|
||||
|
||||
@@ -1749,8 +1749,10 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
} // ✅ INITIAL, VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
|
||||
onKeyDown={handleKeydown}
|
||||
spotlightId={SPOTLIGHT_IDS.SEARCH_INPUT_BOX}
|
||||
// ✅ [251026] ADD: Spotlight 포커스 네비게이션 - data-spotlight-down 속성 사용
|
||||
data-spotlight-down={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ? 'howAboutThese-seeMore' : undefined}
|
||||
// 🎯 [포커스 중첩 해결] SearchResultsContainer로 포커스 전달
|
||||
// SearchResultsContainer가 Spotlight 컨테이너이므로, 포커스가 들어오면
|
||||
// enterTo: 'last-focused' 설정에 의해 자동으로 HowAboutThese.small의 SEE MORE로 이동
|
||||
data-spotlight-down={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ? 'search-results-container' : undefined}
|
||||
// 🎯 HowAboutThese 포커스 관리 - 포커스가 검색 입력 영역으로 올 때 감지
|
||||
onSpotlightUp={handleSearchInputFocus}
|
||||
onSpotlightLeft={handleSearchInputFocus}
|
||||
@@ -1768,8 +1770,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// onFocus={onFocusMic}
|
||||
onKeyDown={handleMicKeyDown}
|
||||
spotlightId={SPOTLIGHT_IDS.MICROPHONE_BUTTON}
|
||||
// ✅ [251026] ADD: Spotlight 포커스 네비게이션 - data-spotlight-down 속성 사용
|
||||
data-spotlight-down={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ? 'howAboutThese-seeMore' : undefined}
|
||||
// 🎯 [포커스 중첩 해결] SearchResultsContainer로 포커스 전달
|
||||
data-spotlight-down={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ? 'search-results-container' : undefined}
|
||||
// 🎯 HowAboutThese 포커스 관리 - 포커스가 마이크 버튼으로 올 때 감지
|
||||
onSpotlightUp={handleSearchInputFocus}
|
||||
onSpotlightLeft={handleSearchInputFocus}
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import downBtnImg from '../../../assets/images/btn/search_btn_down_arrow.png';
|
||||
@@ -215,6 +216,13 @@ const SearchResultsNew = ({
|
||||
// Spottable 컴포넌트 캐싱하여 메모리 누수 방지
|
||||
const SpottableDiv = useMemo(() => Spottable('div'), []);
|
||||
|
||||
// 🎯 [포커스 중첩 해결] SearchResultsNew를 Spotlight 컨테이너로 감싸기
|
||||
// 이렇게 하면 외부에서 들어온 포커스를 자식 요소(HowAboutThese.small)로 자동 전달
|
||||
const SearchResultsContainer = useMemo(
|
||||
() => SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'),
|
||||
[]
|
||||
);
|
||||
|
||||
// ❌ [251026] DEPRECATED: 탭 키다운 핸들러 - Spotlight 속성으로 대체됨
|
||||
// Spotlight의 data-spotlight-up 속성으로 처리하도록 변경
|
||||
// 기존 코드 보존 (향후 필요시 참고용)
|
||||
@@ -353,7 +361,11 @@ const SearchResultsNew = ({
|
||||
// 이제는 SearchPanel에서 통합 포커스 로직으로 관리됨
|
||||
|
||||
return (
|
||||
<div className={css.searchBox}>
|
||||
<SearchResultsContainer
|
||||
className={css.searchBox}
|
||||
spotlightId="search-results-container"
|
||||
data-wheel-point="true"
|
||||
>
|
||||
{/* HowAboutThese Small 버전 - relativeQueries가 존재할 때만 표시 */}
|
||||
{relativeQueries && relativeQueries.length > 0 && (
|
||||
<div>
|
||||
@@ -445,7 +457,7 @@ const SearchResultsNew = ({
|
||||
<CustomImage className={css.btnImg} src={upBtnImg} alt="Up arrow" />
|
||||
</SpottableDiv>
|
||||
</div>
|
||||
</div>
|
||||
</SearchResultsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user