diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less index 2733dc30..a3c2b99b 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less @@ -1028,3 +1028,12 @@ } } } + +// 비디오 컨테이너 스타일 (modal 영역 지정용) +.videoContainer { + position: relative; + width: 100%; + height: auto; + // PlayerPanel 모달이 이 영역에서만 재생되도록 설정 + z-index: 1; +} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx index 5c958f02..9f2439b5 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx @@ -1,8 +1,9 @@ -import React, { useCallback, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import Spottable from '@enact/spotlight/Spottable'; -import { startVideoPlayer } from '../../../../actions/playActions'; +import { startVideoPlayer, finishVideoPreview } from '../../../../actions/playActions'; import CustomImage from '../../../../components/CustomImage/CustomImage'; +import { panel_names } from '../../../../utils/Config'; import playImg from '../../../../../assets/images/btn/btn-play-thumb-nor.png'; import css from './ProductVideo.module.less'; @@ -11,10 +12,51 @@ const SpottableComponent = Spottable('div'); export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) { const dispatch = useDispatch(); - // Indicator.jsx의 canPlayVideo 로직 이식 + // PlayerPanel 상태 체크를 위한 selectors 추가 (Indicator.jsx와 동일) + const panels = useSelector((state) => state.panels.panels); + const [isLaunchedFromPlayer, setIsLaunchedFromPlayer] = useState(false); + const [focused, setFocused] = useState(false); + + const topPanel = panels[panels.length - 1]; + + // Indicator.jsx의 PlayerPanel 상태 체크 로직 추가 + useEffect(() => { + if ( + topPanel && + topPanel.name === panel_names.PLAYER_PANEL && + topPanel.panelInfo.modal === false + ) { + return; // PlayerPanel이 전체화면 모드일 때는 처리하지 않음 + } + }, [topPanel]); + + // Indicator.jsx의 canPlayVideo 로직 완전 이식 (selectedIndex === 0 조건 포함) const canPlayVideo = useMemo(() => { - return Boolean(productInfo?.prdtMediaUrl && videoUrl); - }, [productInfo, videoUrl]); + return Boolean(productInfo?.prdtMediaUrl); + }, [productInfo]); + + // Indicator.jsx의 modalClassNameChange 로직 추가 + const modalClassNameChange = useCallback(() => { + if (focused) { + return css.videoModal; + } + return ''; + }, [focused]); + + // focus 이벤트 핸들러 추가 (Indicator.jsx와 동일) + const videoContainerOnFocus = useCallback(() => { + if (canPlayVideo) { + setFocused(true); + } + }, [canPlayVideo]); + + const videoContainerOnBlur = useCallback(() => { + if (canPlayVideo) { + setFocused(false); + // ProductVideo에서 포커스가 벗어나면 비디오 재생 종료 + dispatch(finishVideoPreview()); + } + }, [canPlayVideo, dispatch]); // Indicator.jsx의 handleVideoOnClick 로직 완전히 이식 const handleVideoClick = useCallback(() => { @@ -37,15 +79,18 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) { prdtNm: productInfo?.prdtNm, thumbnailUrl: productInfo?.thumbnailUrl960, shptmBanrTpNm: 'MEDIA', - // Indicator.jsx와 동일하게: 클릭 시 전체화면 모드 - modal: false, - modalContainerId: 'product-video-player', // 우리 컴포넌트의 spotlightId - modalClassName: css.videoModal, - spotlightDisable: false, + modal: true, + modalContainerId: 'product-video-player', + modalClassName: modalClassNameChange(), + spotlightDisable: true, }) ); } - }, [dispatch, productInfo, canPlayVideo]); + + if (isLaunchedFromPlayer) { + setIsLaunchedFromPlayer(false); + } + }, [dispatch, productInfo, canPlayVideo, isLaunchedFromPlayer, modalClassNameChange]); if (!canPlayVideo) return null; @@ -53,6 +98,8 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) { 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 a8dceb6b..c072d457 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 @@ -1,19 +1,37 @@ +@import "../../../../style/CommonStyle.module.less"; +@import "../../../../style/utils.module.less"; + .videoContainer { position: relative; - width: 100%; - margin-bottom: 20px; + width: 1114px; // ProductDetail과 동일한 고정 크기 + max-width: 1114px; + height: 670px; // ProductDetail과 동일한 고정 높이 + margin-bottom: 30px; // ProductDetail과 동일한 간격 cursor: pointer; - border-radius: 8px; - overflow: hidden; + background-color: rgba(255, 255, 255, 1); + border-radius: 12px; + box-sizing: border-box; + padding: 6px; // 포커스 테두리를 위한 공간 .videoThumbnailWrapper { position: relative; - width: 100%; + width: 658px; // ProductDetail과 동일한 썸네일 크기 + height: 610px; // ProductDetail과 동일한 썸네일 높이 + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto; .videoThumbnail { - width: 100%; - height: auto; - display: block; + max-width: 100%; + max-height: 100%; + margin: 0; + padding: 0; + border: none; + object-fit: contain; // 비율 유지하며 컨테이너에 맞춤 + background-color: @COLOR_WHITE; + border-radius: 8px; + transition: all 0.2s ease; } .playButtonOverlay { @@ -38,22 +56,29 @@ } } - // Spotlight 포커스 스타일 + // ProductDetail과 동일한 포커스 스타일 &:focus { - outline: 2px solid #0078d4; - outline-offset: 2px; + &::after { + content: ""; + position: absolute; + top: -6px; + left: -6px; + right: -6px; + bottom: -6px; + z-index: 19; + border: 6px solid @PRIMARY_COLOR_RED; + box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5); + border-radius: 12px; + } + + .videoThumbnailWrapper .videoThumbnail { + transform: scale(1.015); // 가로세로 10px 정도 확대 효과 + } .playButtonOverlay { opacity: 0.8; } } - - // webOS TV 환경에서의 포커스 스타일 - &.spottable:focus { - outline: 3px solid #ffffff; - outline-offset: 3px; - box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.3); - } } // 동영상 모달 클래스 (PlayerPanel에서 사용)