[251112] feat: ProductVideroV2 Continuous Video Playing
🕐 커밋 시간: 2025. 11. 12. 15:28:31 📊 변경 통계: • 총 파일: 2개 • 추가: +67줄 • 삭제: -31줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx 🔧 주요 변경 내용: • 소규모 기능 개선
This commit is contained in:
@@ -143,6 +143,16 @@
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.hideWhileFullscreen {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.videoPortalHost {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 전체화면 container (Portal, body에 항상 존재)
|
||||
.fullscreenContainer {
|
||||
position: fixed;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState, useRef } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
stopMediaAutoClose,
|
||||
resetMediaAutoClose,
|
||||
} from '../../../../actions/mediaOverlayActions';
|
||||
import { pauseFullscreenVideo, resumeFullscreenVideo, clearAllVideoTimers } from '../../../../actions/playActions';
|
||||
import {
|
||||
pauseFullscreenVideo,
|
||||
resumeFullscreenVideo,
|
||||
clearAllVideoTimers,
|
||||
} from '../../../../actions/playActions';
|
||||
import css from './ProductVideo.module.less';
|
||||
|
||||
const SpottableComponent = Spottable('div');
|
||||
@@ -80,6 +84,16 @@ export function ProductVideoV2({
|
||||
const videoPlayerWrapperRef = useRef(null);
|
||||
const normalContainerRef = useRef(null);
|
||||
const fullscreenContainerRef = useRef(null);
|
||||
const videoPortalHostRef = useRef(null);
|
||||
|
||||
const ensurePortalHost = useCallback(() => {
|
||||
if (!videoPortalHostRef.current && typeof document !== 'undefined') {
|
||||
const host = document.createElement('div');
|
||||
host.className = css.videoPortalHost;
|
||||
videoPortalHostRef.current = host;
|
||||
}
|
||||
return videoPortalHostRef.current;
|
||||
}, []);
|
||||
|
||||
// 비디오 재생 가능 여부 체크
|
||||
const canPlayVideo = useMemo(() => {
|
||||
@@ -149,33 +163,36 @@ export function ProductVideoV2({
|
||||
}, [canPlayVideo, isPlaying]);
|
||||
|
||||
// 썸네일 클릭 핸들러 - 비디오 재생 시작 + Redux dispatch + MediaPlayer 메서드 호출
|
||||
const handleThumbnailClick = useCallback((e) => {
|
||||
console.log('🎬 [handleThumbnailClick] 썸네일 클릭됨', {
|
||||
canPlayVideo,
|
||||
isPlaying,
|
||||
eventType: e?.type,
|
||||
target: e?.target?.className,
|
||||
bubbles: e?.bubbles,
|
||||
timestamp: new Date().getTime(),
|
||||
});
|
||||
if (canPlayVideo && !isPlaying) {
|
||||
// console.log('[BgVideo] ProductVideoV2 - Starting video playback');
|
||||
console.log('🎬 [handleThumbnailClick] ✅ 비디오 재생 시작');
|
||||
setIsPlaying(true);
|
||||
const handleThumbnailClick = useCallback(
|
||||
(e) => {
|
||||
console.log('🎬 [handleThumbnailClick] 썸네일 클릭됨', {
|
||||
canPlayVideo,
|
||||
isPlaying,
|
||||
eventType: e?.type,
|
||||
target: e?.target?.className,
|
||||
bubbles: e?.bubbles,
|
||||
timestamp: new Date().getTime(),
|
||||
});
|
||||
if (canPlayVideo && !isPlaying) {
|
||||
// console.log('[BgVideo] ProductVideoV2 - Starting video playback');
|
||||
console.log('🎬 [handleThumbnailClick] ✅ 비디오 재생 시작');
|
||||
setIsPlaying(true);
|
||||
|
||||
// 백그라운드 전체화면 비디오 일시정지
|
||||
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
// 백그라운드 전체화면 비디오 일시정지
|
||||
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
|
||||
// Redux: mediaOverlay 상태 초기화 (MediaPlayer 전용)
|
||||
dispatch(setMediaControlShow());
|
||||
dispatch(startMediaAutoClose(3000));
|
||||
// MediaPlayer 직접 제어: controls 표시
|
||||
setTimeout(() => {
|
||||
videoPlayerRef.current?.showControls?.();
|
||||
}, 100);
|
||||
}
|
||||
}, [canPlayVideo, isPlaying, dispatch]);
|
||||
// Redux: mediaOverlay 상태 초기화 (MediaPlayer 전용)
|
||||
dispatch(setMediaControlShow());
|
||||
dispatch(startMediaAutoClose(3000));
|
||||
// MediaPlayer 직접 제어: controls 표시
|
||||
setTimeout(() => {
|
||||
videoPlayerRef.current?.showControls?.();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
[canPlayVideo, isPlaying, dispatch]
|
||||
);
|
||||
|
||||
// 비디오 종료 핸들러 - 썸네일로 복귀 + Redux cleanup + MediaPlayer 메서드 호출
|
||||
const handleVideoEnded = useCallback(() => {
|
||||
@@ -355,7 +372,7 @@ export function ProductVideoV2({
|
||||
|
||||
// FullScreen 모드에서의 MediaPlayer Click 핸들러
|
||||
const handleVideoPlayerClick = useCallback(
|
||||
(e) => {
|
||||
(e) => {
|
||||
if (!isPlaying) return;
|
||||
|
||||
if (!isFullscreen) {
|
||||
@@ -363,7 +380,7 @@ export function ProductVideoV2({
|
||||
e.stopPropagation?.();
|
||||
toggleFullscreen();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// fullscreen: overlay toggle
|
||||
e.preventDefault?.();
|
||||
e.stopPropagation?.();
|
||||
@@ -372,10 +389,10 @@ export function ProductVideoV2({
|
||||
const isCurrentlyVisible = videoPlayerRef.current?.areControlsVisible?.();
|
||||
if (isCurrentlyVisible) {
|
||||
videoPlayerRef.current?.hideControls?.();
|
||||
dispatch(setMediaControlHide()); // MediaOverlay Redux 상태 동기화
|
||||
dispatch(setMediaControlHide()); // MediaOverlay Redux 상태 동기화
|
||||
} else {
|
||||
videoPlayerRef.current?.showControls?.();
|
||||
dispatch(setMediaControlShow()); // MediaOverlay Redux 상태 동기화
|
||||
dispatch(setMediaControlShow()); // MediaOverlay Redux 상태 동기화
|
||||
}
|
||||
},
|
||||
[isPlaying, isFullscreen, toggleFullscreen, dispatch, mediaOverlayState.controls?.visible]
|
||||
@@ -394,34 +411,40 @@ export function ProductVideoV2({
|
||||
}, [mediaOverlayState.controls?.visible, dispatch]);
|
||||
|
||||
// 전체 화면 컨테이너용 마우스 다운 핸들러 (Capture Phase)
|
||||
const handleFullscreenContainerMouseDownCapture = useCallback((e) => {
|
||||
if (!isPlaying || !isFullscreen) return;
|
||||
const handleFullscreenContainerMouseDownCapture = useCallback(
|
||||
(e) => {
|
||||
if (!isPlaying || !isFullscreen) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// toggleOverlayVisibility();
|
||||
}, [isPlaying, isFullscreen, toggleOverlayVisibility]);
|
||||
|
||||
// 전체 화면 키보드 핸들러
|
||||
const handleFullscreenKeyDown = useCallback((e) => {
|
||||
if (!isPlaying || !isFullscreen) return;
|
||||
|
||||
// ESC 키만 오버레이 토글로 처리
|
||||
if (e.key === 'Escape' || e.keyCode === 27) {
|
||||
console.log('🖥️ [Fullscreen Container] ESC 키 - 오버레이 토글 실행');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleOverlayVisibility();
|
||||
return;
|
||||
}
|
||||
// toggleOverlayVisibility();
|
||||
},
|
||||
[isPlaying, isFullscreen, toggleOverlayVisibility]
|
||||
);
|
||||
|
||||
// Enter 키는 기본 동작 허용 (포커스된 요소의 동작 수행)
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
console.log('🖥️ [Fullscreen Container] Enter 키 - 포커스된 요소 동작 허용');
|
||||
// Enter 키는 preventDefault하지 않고 기본 동작 허용
|
||||
return;
|
||||
}
|
||||
}, [isPlaying, isFullscreen, toggleOverlayVisibility]);
|
||||
// 전체 화면 키보드 핸들러
|
||||
const handleFullscreenKeyDown = useCallback(
|
||||
(e) => {
|
||||
if (!isPlaying || !isFullscreen) return;
|
||||
|
||||
// ESC 키만 오버레이 토글로 처리
|
||||
if (e.key === 'Escape' || e.keyCode === 27) {
|
||||
console.log('🖥️ [Fullscreen Container] ESC 키 - 오버레이 토글 실행');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleOverlayVisibility();
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter 키는 기본 동작 허용 (포커스된 요소의 동작 수행)
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
console.log('🖥️ [Fullscreen Container] Enter 키 - 포커스된 요소 동작 허용');
|
||||
// Enter 키는 preventDefault하지 않고 기본 동작 허용
|
||||
return;
|
||||
}
|
||||
},
|
||||
[isPlaying, isFullscreen, toggleOverlayVisibility]
|
||||
);
|
||||
|
||||
// 마우스 다운 (클릭) 이벤트 - capture phase에서 처리
|
||||
const handleVideoPlayerMouseDown = useCallback(
|
||||
@@ -466,20 +489,24 @@ export function ProductVideoV2({
|
||||
useEffect(() => {
|
||||
const handleDocumentClick = (e) => {
|
||||
// ProductVideoV2 관련 요소인 경우만 로깅
|
||||
const isVideoElement = e.target?.closest('[class*="videoContainer"]') ||
|
||||
e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('[class*="videoThumbnail"]');
|
||||
const isVideoElement =
|
||||
e.target?.closest('[class*="videoContainer"]') ||
|
||||
e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('[class*="videoThumbnail"]');
|
||||
|
||||
if (isVideoElement) {
|
||||
console.log('📄 [Document Level] 전역 클릭 감지됨', {
|
||||
eventPhase: e.eventPhase,
|
||||
bubbles: e.bubbles,
|
||||
target: e.target?.className,
|
||||
eventPath: e.composedPath?.().slice(0, 6).map(el => ({
|
||||
tag: el.tagName,
|
||||
className: el.className,
|
||||
id: el.id
|
||||
}))
|
||||
eventPath: e
|
||||
.composedPath?.()
|
||||
.slice(0, 6)
|
||||
.map((el) => ({
|
||||
tag: el.tagName,
|
||||
className: el.className,
|
||||
id: el.id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -490,6 +517,36 @@ export function ProductVideoV2({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isPlaying) return;
|
||||
|
||||
const host = ensurePortalHost();
|
||||
const target = isFullscreen ? fullscreenContainerRef.current : normalContainerRef.current;
|
||||
|
||||
if (host && target && host.parentNode !== target) {
|
||||
target.appendChild(host);
|
||||
}
|
||||
}, [isPlaying, isFullscreen, ensurePortalHost]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) return;
|
||||
|
||||
const host = videoPortalHostRef.current;
|
||||
if (host?.parentNode) {
|
||||
host.parentNode.removeChild(host);
|
||||
}
|
||||
}, [isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
const host = videoPortalHostRef.current;
|
||||
if (host?.parentNode) {
|
||||
host.parentNode.removeChild(host);
|
||||
}
|
||||
videoPortalHostRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// autoPlay 기능: 컴포넌트 마운트 후 500ms 후 자동 재생
|
||||
useEffect(() => {
|
||||
if (autoPlay && canPlayVideo && !isPlaying) {
|
||||
@@ -595,17 +652,10 @@ export function ProductVideoV2({
|
||||
|
||||
const containerProps = useMemo(() => {
|
||||
const baseProps = {
|
||||
onClick: handleVideoContainerClick, // 모든 상태에서 동일하게 적용
|
||||
onClick: handleVideoContainerClick,
|
||||
};
|
||||
|
||||
if (isFullscreen) {
|
||||
return {
|
||||
...baseProps,
|
||||
spotlightRestrict: 'self-only', // 포커스가 밖으로 나가지 않도록
|
||||
spotlightId: 'product-video-v2-fullscreen',
|
||||
onSpotlightDown: handleSpotlightDown, // 전체화면에서도 Down 키 동작
|
||||
};
|
||||
} else if (isPlaying) {
|
||||
if (isPlaying) {
|
||||
return {
|
||||
...baseProps,
|
||||
spotlightId: 'product-video-v2-playing',
|
||||
@@ -615,70 +665,89 @@ export function ProductVideoV2({
|
||||
} else {
|
||||
return baseProps;
|
||||
}
|
||||
}, [isFullscreen, isPlaying, handleVideoContainerClick, handleSpotlightDown, handleContainerKeyDown]);
|
||||
}, [
|
||||
isFullscreen,
|
||||
isPlaying,
|
||||
handleVideoContainerClick,
|
||||
handleSpotlightDown,
|
||||
handleContainerKeyDown,
|
||||
]);
|
||||
|
||||
// ⚠️ 간단한 해결책: 비디오 영역 클릭 시 직접 동작 실행
|
||||
const handleContainerClickFallback = useCallback((e) => {
|
||||
const isThumbnailArea = e.target?.closest('[class*="videoThumbnail"]') ||
|
||||
e.target?.closest('[class*="playButton"]');
|
||||
const handleContainerClickFallback = useCallback(
|
||||
(e) => {
|
||||
const isThumbnailArea =
|
||||
e.target?.closest('[class*="videoThumbnail"]') ||
|
||||
e.target?.closest('[class*="playButton"]');
|
||||
|
||||
const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('[class*="VideoPlayer"]');
|
||||
const isVideoPlayerArea =
|
||||
e.target?.closest('[class*="videoPlayer"]') || e.target?.closest('[class*="VideoPlayer"]');
|
||||
|
||||
// 썸네일 클릭: 비디오 재생 시작
|
||||
if (isThumbnailArea && !isPlaying) {
|
||||
console.log('🎬 [handleContainerClickFallback] 썸네일 클릭 → 비디오 재생 시작');
|
||||
handleThumbnailClick(e);
|
||||
}
|
||||
// 썸네일 클릭: 비디오 재생 시작
|
||||
if (isThumbnailArea && !isPlaying) {
|
||||
console.log('🎬 [handleContainerClickFallback] 썸네일 클릭 → 비디오 재생 시작');
|
||||
handleThumbnailClick(e);
|
||||
}
|
||||
|
||||
// 비디오 재생 중 클릭: 전체화면 토글
|
||||
if (isVideoPlayerArea && isPlaying && !isFullscreen) {
|
||||
console.log('🎬 [handleContainerClickFallback] 비디오 클릭 → 전체화면 토글');
|
||||
toggleFullscreen();
|
||||
}
|
||||
}, [isPlaying, isFullscreen, handleThumbnailClick, toggleFullscreen]);
|
||||
// 비디오 재생 중 클릭: 전체화면 토글
|
||||
if (isVideoPlayerArea && isPlaying && !isFullscreen) {
|
||||
console.log('🎬 [handleContainerClickFallback] 비디오 클릭 → 전체화면 토글');
|
||||
toggleFullscreen();
|
||||
}
|
||||
},
|
||||
[isPlaying, isFullscreen, handleThumbnailClick, toggleFullscreen]
|
||||
);
|
||||
|
||||
// ⚠️ 핵심: TScrollerDetail이 capture phase에서 이벤트를 막으므로,
|
||||
// ProductVideoV2 컨테이너 전체에서 capture phase를 다시 열어줌 (Hooks는 early return 전에)
|
||||
const handleContainerMouseDownCapture = useCallback((e) => {
|
||||
// 비디오 영역인지 확인
|
||||
const isVideoArea = e.currentTarget === containerRef.current;
|
||||
const handleContainerMouseDownCapture = useCallback(
|
||||
(e) => {
|
||||
// 비디오 영역인지 확인
|
||||
const isVideoArea = e.currentTarget === containerRef.current;
|
||||
|
||||
console.log('🔓 [ProductVideoV2 Container] onMouseDownCapture - TScrollerDetail 차단 우회', {
|
||||
isVideoArea,
|
||||
target: e.target?.className,
|
||||
isPlaying,
|
||||
isFullscreen,
|
||||
});
|
||||
console.log('🔓 [ProductVideoV2 Container] onMouseDownCapture - TScrollerDetail 차단 우회', {
|
||||
isVideoArea,
|
||||
target: e.target?.className,
|
||||
isPlaying,
|
||||
isFullscreen,
|
||||
});
|
||||
|
||||
// 전체 화면 모드가 아닐 때만 toggleFullscreen 호출
|
||||
if (!isFullscreen) {
|
||||
toggleFullscreen();
|
||||
// 전체 화면 모드가 아닐 때만 toggleFullscreen 호출
|
||||
if (!isFullscreen) {
|
||||
toggleFullscreen();
|
||||
|
||||
// TScrollerDetail의 onClick 실행을 막기 위해 preventDefault 호출
|
||||
if (isPlaying) {
|
||||
console.log('🔓 [ProductVideoV2] TScrollerDetail onClick 실행 차단');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// TScrollerDetail의 onClick 실행을 막기 위해 preventDefault 호출
|
||||
if (isPlaying) {
|
||||
console.log('🔓 [ProductVideoV2] TScrollerDetail onClick 실행 차단');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
} else {
|
||||
// 전체 화면 모드에서는 오버레이(컨트롤)만 토글
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 전체 화면 모드 - 오버레이 토글 시작');
|
||||
console.log(
|
||||
'🖥️ [ProductVideoV2.OVERLAY-Toggle] 현재 오버레이 상태:',
|
||||
overlayState.controls?.visible
|
||||
);
|
||||
|
||||
try {
|
||||
const result = dispatch(toggleControls());
|
||||
console.log(
|
||||
'🖥️ [ProductVideoV2.OVERLAY-Toggle] toggleControls 액션 디스패치 결과:',
|
||||
result
|
||||
);
|
||||
|
||||
// 디스패치 후 상태 변화 확인 (setTimeout으로 비동기 처리)
|
||||
setTimeout(() => {
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 액션 디스패치 후 상태 확인 필요');
|
||||
}, 10);
|
||||
} catch (error) {
|
||||
console.error('🖥️ [ProductVideoV2.OVERLAY-ERROR] toggleControls 디스패치 에러:', error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 전체 화면 모드에서는 오버레이(컨트롤)만 토글
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 전체 화면 모드 - 오버레이 토글 시작');
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 현재 오버레이 상태:', overlayState.controls?.visible);
|
||||
|
||||
try {
|
||||
const result = dispatch(toggleControls());
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] toggleControls 액션 디스패치 결과:', result);
|
||||
|
||||
// 디스패치 후 상태 변화 확인 (setTimeout으로 비동기 처리)
|
||||
setTimeout(() => {
|
||||
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 액션 디스패치 후 상태 확인 필요');
|
||||
}, 10);
|
||||
} catch (error) {
|
||||
console.error('🖥️ [ProductVideoV2.OVERLAY-ERROR] toggleControls 디스패치 에러:', error);
|
||||
}
|
||||
}
|
||||
}, [isPlaying, isFullscreen, dispatch, toggleControls]);
|
||||
},
|
||||
[isPlaying, isFullscreen, dispatch, toggleControls]
|
||||
);
|
||||
|
||||
// Early return: 비디오 재생 불가능한 경우
|
||||
if (!canPlayVideo) return null;
|
||||
@@ -687,17 +756,16 @@ export function ProductVideoV2({
|
||||
// 전체화면: SpotlightContainer (self-only)
|
||||
// 일반 재생: SpottableComponent (포커스 가능)
|
||||
// 썸네일: div
|
||||
const ContainerComponent = isFullscreen
|
||||
? SpotlightContainer
|
||||
: isPlaying
|
||||
? SpottableComponent
|
||||
: 'div';
|
||||
const ContainerComponent = isPlaying ? SpottableComponent : 'div';
|
||||
|
||||
// VideoPlayer 컴포넌트 생성 함수
|
||||
const renderVideoPlayer = () => {
|
||||
if (!isPlaying) return null;
|
||||
|
||||
return (
|
||||
const host = ensurePortalHost();
|
||||
if (!host) return null;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<div
|
||||
ref={videoPlayerWrapperRef}
|
||||
className={`${css.videoPlayerWrapper} ${isFullscreen ? css.fullscreenPlayer : ''}`}
|
||||
@@ -717,9 +785,10 @@ export function ProductVideoV2({
|
||||
onMouseDownCapture={(e) => {
|
||||
// ⚠️ 핵심: VideoPlayer 내부(실제 비디오 영역)에서만 이벤트를 살리고,
|
||||
// 그 외 wrapper 영역에서만 preventDefault
|
||||
const isVideoElement = e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('video') ||
|
||||
e.target?.closest('[class*="react-player"]');
|
||||
const isVideoElement =
|
||||
e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('video') ||
|
||||
e.target?.closest('[class*="react-player"]');
|
||||
|
||||
console.log('🎬 [videoPlayerWrapper] onMouseDownCapture 실행됨', {
|
||||
isFullscreen,
|
||||
@@ -777,7 +846,8 @@ export function ProductVideoV2({
|
||||
<track kind="subtitles" src={productInfo?.prdtMediaSubtitlUrl} default />
|
||||
)}
|
||||
</VideoPlayer>
|
||||
</div>
|
||||
</div>,
|
||||
host
|
||||
);
|
||||
};
|
||||
|
||||
@@ -787,7 +857,7 @@ export function ProductVideoV2({
|
||||
<ContainerComponent
|
||||
{...containerProps}
|
||||
ref={containerRef}
|
||||
className={`${css.videoContainer} ${isFullscreen ? css.fullscreen : ''}`}
|
||||
className={`${css.videoContainer} ${isFullscreen ? css.hideWhileFullscreen : ''}`}
|
||||
onMouseDownCapture={handleContainerMouseDownCapture}
|
||||
onClick={handleContainerClickFallback}
|
||||
>
|
||||
@@ -818,8 +888,7 @@ export function ProductVideoV2({
|
||||
</div>
|
||||
</div>
|
||||
</SpottableComponent>
|
||||
) : !isFullscreen ? (
|
||||
// 일반 재생 모드: VideoPlayer를 직접 렌더링
|
||||
) : (
|
||||
<div
|
||||
ref={normalContainerRef}
|
||||
className={css.normalPlayerContainer}
|
||||
@@ -833,10 +902,9 @@ export function ProductVideoV2({
|
||||
handleVideoPlayerClick(e);
|
||||
}}
|
||||
onMouseDownCapture={(e) => {
|
||||
// ⚠️ 핵심: VideoPlayer 영역 내에서만 이벤트를 살리고,
|
||||
// 그 외 영역(스크롤 영역)에서만 preventDefault
|
||||
const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('[class*="VideoPlayer"]');
|
||||
const isVideoPlayerArea =
|
||||
e.target?.closest('[class*="videoPlayer"]') ||
|
||||
e.target?.closest('[class*="VideoPlayer"]');
|
||||
|
||||
console.log('🎬 [normalContainerRef] onMouseDownCapture 실행됨', {
|
||||
isPlaying,
|
||||
@@ -845,39 +913,34 @@ export function ProductVideoV2({
|
||||
target: e.target?.className,
|
||||
});
|
||||
|
||||
// VideoPlayer가 아닌 영역(스크롤 영역)에서만 preventDefault
|
||||
if (!isVideoPlayerArea) {
|
||||
e.preventDefault();
|
||||
console.log('🛑 [normalContainerRef] preventDefault - 스크롤 영역에서의 클릭');
|
||||
} else {
|
||||
// VideoPlayer 영역이면 이벤트를 전파시켜 click이 정상 발생하도록
|
||||
console.log('✅ [normalContainerRef] 이벤트 전파 허용 - VideoPlayer 영역');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{renderVideoPlayer()}
|
||||
</div>
|
||||
) : null}
|
||||
/>
|
||||
)}
|
||||
</ContainerComponent>
|
||||
|
||||
{/* 전체화면 container (Portal) */}
|
||||
{isPlaying && isFullscreen && ReactDOM.createPortal(
|
||||
<SpotlightContainer
|
||||
spotlightRestrict="self-only"
|
||||
spotlightId="product-video-v2-fullscreen-portal"
|
||||
onMouseDownCapture={handleFullscreenContainerMouseDownCapture}
|
||||
onKeyDown={handleFullscreenKeyDown}
|
||||
// onClick={handleFullscreenClick}
|
||||
>
|
||||
<div
|
||||
ref={fullscreenContainerRef}
|
||||
className={css.fullscreenContainer}
|
||||
{isPlaying &&
|
||||
isFullscreen &&
|
||||
ReactDOM.createPortal(
|
||||
<SpotlightContainer
|
||||
spotlightRestrict="self-only"
|
||||
spotlightId="product-video-v2-fullscreen-portal"
|
||||
onMouseDownCapture={handleFullscreenContainerMouseDownCapture}
|
||||
onKeyDown={handleFullscreenKeyDown}
|
||||
// onClick={handleFullscreenClick}
|
||||
>
|
||||
{renderVideoPlayer()}
|
||||
</div>
|
||||
</SpotlightContainer>,
|
||||
document.body
|
||||
)}
|
||||
<div ref={fullscreenContainerRef} className={css.fullscreenContainer} />
|
||||
</SpotlightContainer>,
|
||||
document.body
|
||||
)}
|
||||
|
||||
{renderVideoPlayer()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user