From 74619cba4e9b64674e33d9759d33c058ad9dc229 Mon Sep 17 00:00:00 2001 From: optrader Date: Thu, 13 Nov 2025 10:59:11 +0900 Subject: [PATCH] [251113] style: views - TReactPlayer.jsx, ProductVideo.module.less, Pro... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 13. 10:59:09 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 5๊ฐœ โ€ข ์ถ”๊ฐ€: +173์ค„ โ€ข ์‚ญ์ œ: -76์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  --- .../components/VideoPlayer/TReactPlayer.jsx | 24 ++-- .../ProductVideo/ProductVideo.module.less | 50 ++++++-- .../ProductVideo/ProductVideo.v2.jsx | 47 +++++-- .../src/views/MediaPanel/MediaPanel.jsx | 116 ++++++++++-------- .../views/MediaPanel/MediaPanel.module.less | 12 ++ 5 files changed, 173 insertions(+), 76 deletions(-) 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 ( {currentPlayingUrl && ( - - {typeof window === 'object' && window.PalmSystem && ( - +
} - + > + + {typeof window === 'object' && window.PalmSystem && ( + + )} + {isSubtitleActive && + !panelInfo.modal && + typeof window === 'object' && + window.PalmSystem && + currentSubtitleUrl && } + +
)}
diff --git a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less index d2aef4d0..f392f425 100644 --- a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less +++ b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less @@ -60,3 +60,15 @@ } } } + +.videoPlayerWrapper { + width: 100%; + height: 100%; +} + +.youtubeSafe { + :global(.react-player), + :global(.react-player iframe) { + pointer-events: none !important; + } +}