diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx index 79c1e6b1..63e2ecf5 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx @@ -29,14 +29,22 @@ export default function TReactPlayer({ const handleEvent = useCallback( (type) => (ev) => { - if (type === "onReady") { - if (videoRef) { - const videoNode = playerRef.current.getInternalPlayer(); - videoRef(videoNode); - if ( - videoNode.tagName && - !Object.prototype.hasOwnProperty.call(videoNode, "proportionPlayed") - ) { + if (type === "onReady") { + if (videoRef) { + const videoNode = playerRef.current.getInternalPlayer(); + videoRef(videoNode); + const iframeEl = + typeof playerRef.current?.getInternalPlayer === "function" + ? playerRef.current.getInternalPlayer("iframe") + : null; + if (iframeEl) { + iframeEl.setAttribute("tabIndex", "-1"); + iframeEl.setAttribute("aria-hidden", "true"); + } + if ( + videoNode.tagName && + !Object.prototype.hasOwnProperty.call(videoNode, "proportionPlayed") + ) { Object.defineProperties(videoNode, { error: { get: function () { diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less index 9ffffb01..580d6b10 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less @@ -98,16 +98,21 @@ } } +.youtubeSafe { + :global(.react-player), + :global(.react-player iframe) { + pointer-events: none !important; + } +} + // 전체화면 모드 (ProductVideoV2 엔터키 토글) .fullscreen { position: fixed !important; top: 0 !important; left: 0 !important; - width: 1920px !important; - height: 1080px !important; - max-width: 1920px !important; - z-index: 99999 !important; // 최상위 레이어 - margin: 0 !important; + width: 100vw !important; + height: 100vh !important; + z-index: 99999 !important; // 전체 화면 기준 border-radius: 0 !important; background-color: @COLOR_BLACK; @@ -115,12 +120,39 @@ border-radius: 0; } - // 전체화면 모드에서는 포커스가 밖으로 나가지 않도록 - // Spotlight container가 이 영역만 관리 + // 전체화면 모드에서 포커스가 벗어나지 않도록 + // Spotlight container는 별도 관리 } .fullscreenPlayer { + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100vw !important; + height: 100vh !important; border-radius: 0 !important; + z-index: 100000 !important; + background-color: @COLOR_BLACK; + + :global(.videoPlayer) { + width: 100% !important; + height: 100% !important; + background-color: @COLOR_BLACK; + } + + :global(video) { + width: 100% !important; + height: 100% !important; + object-fit: cover; + background-color: @COLOR_BLACK; + } + + :global(.react-player), + :global(.react-player iframe) { + width: 100% !important; + height: 100% !important; + background-color: @COLOR_BLACK; + } } .videoThumbnailContainer { @@ -158,8 +190,8 @@ position: fixed; top: 0; left: 0; - width: 1920px; - height: 1080px; + width: 100vw; + height: 100vh; z-index: 99999; background-color: @COLOR_BLACK; diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx index 93ffe9eb..025785f7 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx @@ -548,7 +548,7 @@ export function ProductVideoV2({ e.preventDefault(); e.stopPropagation(); - // toggleOverlayVisibility(); + toggleOverlayVisibility(); }, [isPlaying, isFullscreen, toggleOverlayVisibility] ); @@ -558,23 +558,29 @@ export function ProductVideoV2({ (e) => { if (!isPlaying || !isFullscreen) return; - // ESC 키만 오버레이 토글로 처리 + const isOverlayVisible = mediaOverlayState.controls?.visible; + if (e.key === 'Escape' || e.keyCode === 27) { - debugLog('🖥️ [Fullscreen Container] ESC 키 - 오버레이 토글 실행'); + debugLog('[Fullscreen Container] ESC key - toggling overlay'); e.preventDefault(); e.stopPropagation(); toggleOverlayVisibility(); return; } - // Enter 키는 기본 동작 허용 (포커스된 요소의 동작 수행) if (e.key === 'Enter' || e.keyCode === 13) { - debugLog('🖥️ [Fullscreen Container] Enter 키 - 포커스된 요소 동작 허용'); - // Enter 키는 preventDefault하지 않고 기본 동작 허용 - return; + if (!isOverlayVisible) { + debugLog('[Fullscreen Container] Enter key - overlay hidden, showing controls'); + e.preventDefault(); + e.stopPropagation(); + toggleOverlayVisibility(); + return; + } + + debugLog('[Fullscreen Container] Enter key - overlay visible, allow default behavior'); } }, - [isPlaying, isFullscreen, toggleOverlayVisibility] + [isPlaying, isFullscreen, toggleOverlayVisibility, mediaOverlayState.controls?.visible] ); // 마우스 다운 (클릭) 이벤트 - capture phase에서 처리 @@ -764,6 +770,17 @@ export function ProductVideoV2({ } }, [isFullscreen, isPlaying]); + useEffect(() => { + if (!isPlaying) return; + + if (isFullscreen && isYoutube) { + videoPlayerRef.current?.showControls?.(); + dispatch(setMediaControlShow()); + dispatch(resetMediaAutoClose()); + dispatch(startMediaAutoClose(5000)); + } + }, [dispatch, isFullscreen, isPlaying, isYoutube]); + useEffect(() => { const wasFullscreen = prevFullscreenRef.current; if (wasFullscreen && !isFullscreen && isPlaying) { @@ -924,11 +941,19 @@ export function ProductVideoV2({ const host = ensurePortalHost(); if (!host) return null; + const shouldDisableIframeInteraction = isYoutube && isFullscreen; + const wrapperClasses = [ + css.videoPlayerWrapper, + isFullscreen ? css.fullscreenPlayer : '', + shouldDisableIframeInteraction ? css.youtubeSafe : '', + ] + .filter(Boolean) + .join(' '); return ReactDOM.createPortal(
); } + + + + diff --git a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx index 64c59765..573356ea 100644 --- a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx +++ b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx @@ -100,6 +100,14 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props } }, [panelInfo?.isPaused, panelInfo?.modal]); + useEffect(() => { + if (!videoPlayer.current) return; + if (!isYoutube) return; + if (panelInfo?.modal) return; + + videoPlayer.current.showControls?.(); + }, [isYoutube, panelInfo?.modal]); + const getPlayer = useCallback((ref) => { videoPlayer.current = ref; }, []); @@ -463,6 +471,7 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // minimized 상태일 때는 spotlightRestrict 해제 (포커스 이동 허용) const containerSpotlightRestrict = panelInfo.isMinimized ? 'none' : 'self-only'; + const shouldDisableIframeInteraction = isYoutube && !panelInfo.modal; return (