From d8dce0a89d5d8b714594a1ddd3a832a5e353f5a9 Mon Sep 17 00:00:00 2001 From: optrader Date: Wed, 19 Nov 2025 17:35:54 +0900 Subject: [PATCH] [251119] feat: FloatingGradientLayer..Experimental MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🕐 커밋 시간: 2025. 11. 19. 17:35:53 📊 변경 통계: • 총 파일: 8개 • 추가: +43줄 📁 추가된 파일: + com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx + com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.module.less + com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.v2.jsx 📝 수정된 파일: ~ com.twin.app.shoptime/src/App/App.js ~ com.twin.app.shoptime/src/actions/panelActions.js ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • UI 컴포넌트 아키텍처 개선 • 소규모 기능 개선 • 모듈 구조 개선 --- com.twin.app.shoptime/src/App/App.js | 20 ++ .../src/actions/panelActions.js | 6 + .../FloatingGradientBackground.jsx | 60 +++++ .../FloatingGradientBackground.module.less | 65 +++++ .../src/views/DetailPanel/DetailPanel.jsx | 15 ++ .../DetailPanelBackground.v2.jsx | 230 ++++++++++++++++++ .../src/views/HomePanel/HomePanel.jsx | 1 + .../src/views/HomePanel/HomePanel.module.less | 1 + 8 files changed, 398 insertions(+) create mode 100644 com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx create mode 100644 com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.module.less create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.v2.jsx diff --git a/com.twin.app.shoptime/src/App/App.js b/com.twin.app.shoptime/src/App/App.js index a52e5f09..91e499b9 100644 --- a/com.twin.app.shoptime/src/App/App.js +++ b/com.twin.app.shoptime/src/App/App.js @@ -46,6 +46,7 @@ import { enqueuePanelHistory } from '../actions/panelHistoryActions'; import NotSupportedVersion from '../components/NotSupportedVersion/NotSupportedVersion'; import ToastContainer from '../components/TToast/ToastContainer'; import GlobalPopup from '../components/GlobalPopup/GlobalPopup'; +import FloatingGradientBackground from '../components/FloatingGradientBackground/FloatingGradientBackground'; import usePrevious from '../hooks/usePrevious'; import { lunaTest } from '../lunaSend/lunaTest'; import { store } from '../store/store'; @@ -441,6 +442,9 @@ if (typeof Spotlight !== 'undefined' && Spotlight.addEventListener) { function AppBase(props) { const dispatch = useDispatch(); + + // 그라데이션 배경 표시 상태 관리 (기본적으로 숨김) + const [showGradientBackground, setShowGradientBackground] = React.useState(false); const httpHeader = useSelector((state) => state.common.httpHeader); const httpHeaderRef = useRef(httpHeader); const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion); @@ -892,8 +896,24 @@ function AppBase(props) { ); }, [dispatch, initService]); + // ✅ 그라데이션 배경 제어 함수를 전역에 노출 + useEffect(() => { + window.toggleFloatingGradient = setShowGradientBackground; + window.showFloatingGradient = () => setShowGradientBackground(true); + window.hideFloatingGradient = () => setShowGradientBackground(false); + + return () => { + delete window.toggleFloatingGradient; + delete window.showFloatingGradient; + delete window.hideFloatingGradient; + }; + }, []); + return ( + {/* 항상 메모리에 로드되는 떠 있는 그라데이션 배경 */} + + {webOSVersion === '' ? null : Number(webOSVersion) < 4 ? ( ) : ( diff --git a/com.twin.app.shoptime/src/actions/panelActions.js b/com.twin.app.shoptime/src/actions/panelActions.js index 72bd8d59..e7d492a1 100644 --- a/com.twin.app.shoptime/src/actions/panelActions.js +++ b/com.twin.app.shoptime/src/actions/panelActions.js @@ -103,6 +103,12 @@ export const navigateToDetail = ({ timestamp: Date.now(), }); + // ✅ 그라데이션 배경 표시 - HomePanel→DetailPanel 전환 시 + if (window.showFloatingGradient) { + window.showFloatingGradient(); + console.log('[navigateToDetail] Floating gradient background shown'); + } + // sourceMenu에 따른 사전 처리 switch (sourceMenu) { case SOURCE_MENUS.HOME_BEST_SELLER: diff --git a/com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx b/com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx new file mode 100644 index 00000000..72066e04 --- /dev/null +++ b/com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx @@ -0,0 +1,60 @@ +import React, { useCallback, useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; + +import css from './FloatingGradientBackground.module.less'; + +/** + * HomePanel과 분리된 별도의 그라데이션 배경 + * React Portal로 전역 body에 직접 렌더링됨 + * 항상 메모리에 로드되지만 visible prop으로 표시/숨김 제어 + */ +export default function FloatingGradientBackground({ visible = false }) { + // 전역 DOM에 렌더링할 container 참조 + const containerRef = useRef(null); + + // portal container 생성 (마운트 시 한 번만) + useEffect(() => { + // body에 직접 div 추가 + const portalContainer = document.createElement('div'); + portalContainer.id = 'floating-gradient-background-container'; + portalContainer.style.position = 'fixed'; + portalContainer.style.top = '0'; + portalContainer.style.left = '0'; + portalContainer.style.width = '100%'; + portalContainer.style.height = '100%'; + portalContainer.style.pointerEvents = 'none'; + portalContainer.style.zIndex = '20'; // HomePanel과 DetailPanel 사이 (HomePanel < 20 < DetailPanel(21)) + + document.body.appendChild(portalContainer); + containerRef.current = portalContainer; + + console.log('[FloatingGradientBackground] Portal container created'); + + return () => { + // 언마운트 시 portal container 제거 + if (containerRef.current && containerRef.current.parentNode) { + containerRef.current.parentNode.removeChild(containerRef.current); + console.log('[FloatingGradientBackground] Portal container removed'); + } + }; + }, []); + + // Portal로 렌더링될 그라데이션 컴포넌트 + const gradientContent = ( +