[250929] feat: ProductAllSection 비디오 기능 수정
This commit is contained in:
@@ -1028,3 +1028,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 비디오 컨테이너 스타일 (modal 영역 지정용)
|
||||||
|
.videoContainer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
// PlayerPanel 모달이 이 영역에서만 재생되도록 설정
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo, useState, useEffect } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
import { startVideoPlayer } from '../../../../actions/playActions';
|
import { startVideoPlayer, finishVideoPreview } from '../../../../actions/playActions';
|
||||||
import CustomImage from '../../../../components/CustomImage/CustomImage';
|
import CustomImage from '../../../../components/CustomImage/CustomImage';
|
||||||
|
import { panel_names } from '../../../../utils/Config';
|
||||||
import playImg from '../../../../../assets/images/btn/btn-play-thumb-nor.png';
|
import playImg from '../../../../../assets/images/btn/btn-play-thumb-nor.png';
|
||||||
import css from './ProductVideo.module.less';
|
import css from './ProductVideo.module.less';
|
||||||
|
|
||||||
@@ -11,10 +12,51 @@ const SpottableComponent = Spottable('div');
|
|||||||
export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
|
export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
|
||||||
const dispatch = useDispatch();
|
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(() => {
|
const canPlayVideo = useMemo(() => {
|
||||||
return Boolean(productInfo?.prdtMediaUrl && videoUrl);
|
return Boolean(productInfo?.prdtMediaUrl);
|
||||||
}, [productInfo, videoUrl]);
|
}, [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 로직 완전히 이식
|
// Indicator.jsx의 handleVideoOnClick 로직 완전히 이식
|
||||||
const handleVideoClick = useCallback(() => {
|
const handleVideoClick = useCallback(() => {
|
||||||
@@ -37,15 +79,18 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
|
|||||||
prdtNm: productInfo?.prdtNm,
|
prdtNm: productInfo?.prdtNm,
|
||||||
thumbnailUrl: productInfo?.thumbnailUrl960,
|
thumbnailUrl: productInfo?.thumbnailUrl960,
|
||||||
shptmBanrTpNm: 'MEDIA',
|
shptmBanrTpNm: 'MEDIA',
|
||||||
// Indicator.jsx와 동일하게: 클릭 시 전체화면 모드
|
modal: true,
|
||||||
modal: false,
|
modalContainerId: 'product-video-player',
|
||||||
modalContainerId: 'product-video-player', // 우리 컴포넌트의 spotlightId
|
modalClassName: modalClassNameChange(),
|
||||||
modalClassName: css.videoModal,
|
spotlightDisable: true,
|
||||||
spotlightDisable: false,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [dispatch, productInfo, canPlayVideo]);
|
|
||||||
|
if (isLaunchedFromPlayer) {
|
||||||
|
setIsLaunchedFromPlayer(false);
|
||||||
|
}
|
||||||
|
}, [dispatch, productInfo, canPlayVideo, isLaunchedFromPlayer, modalClassNameChange]);
|
||||||
|
|
||||||
if (!canPlayVideo) return null;
|
if (!canPlayVideo) return null;
|
||||||
|
|
||||||
@@ -53,6 +98,8 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
|
|||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={css.videoContainer}
|
className={css.videoContainer}
|
||||||
onClick={handleVideoClick}
|
onClick={handleVideoClick}
|
||||||
|
onFocus={videoContainerOnFocus}
|
||||||
|
onBlur={videoContainerOnBlur}
|
||||||
spotlightId="product-video-player"
|
spotlightId="product-video-player"
|
||||||
aria-label={`${productInfo?.prdtNm} 동영상 재생`}
|
aria-label={`${productInfo?.prdtNm} 동영상 재생`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,19 +1,37 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
.videoContainer {
|
.videoContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 1114px; // ProductDetail과 동일한 고정 크기
|
||||||
margin-bottom: 20px;
|
max-width: 1114px;
|
||||||
|
height: 670px; // ProductDetail과 동일한 고정 높이
|
||||||
|
margin-bottom: 30px; // ProductDetail과 동일한 간격
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 8px;
|
background-color: rgba(255, 255, 255, 1);
|
||||||
overflow: hidden;
|
border-radius: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 6px; // 포커스 테두리를 위한 공간
|
||||||
|
|
||||||
.videoThumbnailWrapper {
|
.videoThumbnailWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 658px; // ProductDetail과 동일한 썸네일 크기
|
||||||
|
height: 610px; // ProductDetail과 동일한 썸네일 높이
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
.videoThumbnail {
|
.videoThumbnail {
|
||||||
width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
max-height: 100%;
|
||||||
display: block;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
object-fit: contain; // 비율 유지하며 컨테이너에 맞춤
|
||||||
|
background-color: @COLOR_WHITE;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playButtonOverlay {
|
.playButtonOverlay {
|
||||||
@@ -38,22 +56,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spotlight 포커스 스타일
|
// ProductDetail과 동일한 포커스 스타일
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: 2px solid #0078d4;
|
&::after {
|
||||||
outline-offset: 2px;
|
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 {
|
.playButtonOverlay {
|
||||||
opacity: 0.8;
|
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에서 사용)
|
// 동영상 모달 클래스 (PlayerPanel에서 사용)
|
||||||
|
|||||||
Reference in New Issue
Block a user