[250929] feat: ProductAllSection 비디오 기능 수정

This commit is contained in:
2025-09-29 11:47:58 +09:00
parent 15c1176861
commit 9999a59fc7
3 changed files with 111 additions and 30 deletions

View File

@@ -1028,3 +1028,12 @@
} }
} }
} }
// 비디오 컨테이너 스타일 (modal 영역 지정용)
.videoContainer {
position: relative;
width: 100%;
height: auto;
// PlayerPanel 모달이 이 영역에서만 재생되도록 설정
z-index: 1;
}

View File

@@ -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} 동영상 재생`}
> >

View File

@@ -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에서 사용)