[251115] fix: DetailPanel FullScreen Size
🕐 커밋 시간: 2025. 11. 15. 22:53:23 📊 변경 통계: • 총 파일: 6개 • 추가: +211줄 • 삭제: -76줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/mediaActions.js ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.module.less ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.module.less ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/mediaActions.js (javascript): ✅ Added: minimizeModalMedia() 📄 com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.module.less (unknown): ✅ Added: gradient() ❌ Deleted: gradient() 📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript): ✅ Added: handleScrollReset() 🔄 Modified: extractProductMeta() 📄 com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx (javascript): ✅ Added: attemptRestore() 🔄 Modified: normalizeModalStyle() 📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • UI 컴포넌트 아키텍처 개선
This commit is contained in:
@@ -284,6 +284,14 @@ export const minimizeModalMedia = () => (dispatch, getState) => {
|
||||
export const restoreModalMedia = () => (dispatch, getState) => {
|
||||
const panels = getState().panels.panels;
|
||||
|
||||
if (typeof window !== 'undefined' && window.detailPanelScrollTop !== 0) {
|
||||
console.log(
|
||||
'[restoreModalMedia] Blocked restore because detail panel scroll not zero:',
|
||||
window.detailPanelScrollTop
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log('[Restore]] ========== Called ==========');
|
||||
// console.log('[Restore] Total panels:', panels.length);
|
||||
// console.log(
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
@import "~@enact/sandstone/styles/skin.less";
|
||||
@import "../../style/utils.module.less";
|
||||
@import "../../style/CommonStyle.module.less";
|
||||
.fullscreen .videoPlayer,
|
||||
.videoPlayer {
|
||||
// Set by counting the IconButtons inside the side components.
|
||||
--liftDistance: 0px;
|
||||
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
:focus {
|
||||
@@ -19,10 +21,17 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.video {
|
||||
height: calc(100% - 4px);
|
||||
width: calc(100% - 4px);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: #000;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.media {
|
||||
@@ -48,6 +57,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
--media-width: 100vw;
|
||||
--media-height: 100vh;
|
||||
}
|
||||
|
||||
.fullscreen .videoPlayer .media {
|
||||
--media-width: 100vw;
|
||||
--media-height: 100vh;
|
||||
}
|
||||
|
||||
.preloadVideo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -537,6 +537,29 @@ export default function ProductAllSection({
|
||||
}, []);
|
||||
|
||||
const scrollContainerRef = useRef(null);
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.detailPanelScrollTop = 0;
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const handleScrollReset = () => {
|
||||
const container = scrollContainerRef.current;
|
||||
if (!container) return;
|
||||
if (typeof container.scrollTo === 'function') {
|
||||
container.scrollTo({ top: 0, behavior: 'auto' });
|
||||
} else if (typeof container.scrollTop === 'number') {
|
||||
container.scrollTop = 0;
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof document === 'undefined') return () => {};
|
||||
|
||||
document.addEventListener('detailpanel-scroll-reset', handleScrollReset);
|
||||
return () => {
|
||||
document.removeEventListener('detailpanel-scroll-reset', handleScrollReset);
|
||||
};
|
||||
}, []);
|
||||
const productDetailRef = useRef(null); //높이값 변경때문
|
||||
const descriptionRef = useRef(null);
|
||||
const reviewRef = useRef(null);
|
||||
@@ -666,6 +689,9 @@ export default function ProductAllSection({
|
||||
const handleScroll = useCallback(
|
||||
(e) => {
|
||||
const currentScrollTop = e.scrollTop;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.detailPanelScrollTop = Math.max(0, currentScrollTop);
|
||||
}
|
||||
const prevScrollTop = prevScrollTopRef.current;
|
||||
|
||||
scrollPositionRef.current = currentScrollTop;
|
||||
|
||||
@@ -913,6 +913,7 @@ const MediaPanel = React.forwardRef(
|
||||
if (!panelInfo.modal) {
|
||||
dispatch(PanelActions.popPanel());
|
||||
dispatch(changeAppStatus({ cursorVisible: false }));
|
||||
document?.dispatchEvent?.(new CustomEvent('detailpanel-scroll-reset'));
|
||||
|
||||
//딮링크로 플레이어 진입 후 이전버튼 클릭시
|
||||
if (panels.length === 1) {
|
||||
@@ -1120,9 +1121,34 @@ const MediaPanel = React.forwardRef(
|
||||
dispatch(minimizeModalMedia());
|
||||
}, [dispatch]);
|
||||
|
||||
const restoreViaRef = useCallback(() => {
|
||||
const requestDetailScrollReset = useCallback(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
document.dispatchEvent(new CustomEvent('detailpanel-scroll-reset'));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const canRestoreFromDetailPanel = useCallback(() => {
|
||||
if (typeof window === 'undefined') return true;
|
||||
return window.detailPanelScrollTop === 0;
|
||||
}, []);
|
||||
|
||||
const restoreWhenScrollZero = useCallback(() => {
|
||||
let attempts = 0;
|
||||
const attemptRestore = () => {
|
||||
if (canRestoreFromDetailPanel()) {
|
||||
dispatch(restoreModalMedia());
|
||||
}, [dispatch]);
|
||||
} else if (attempts < 5) {
|
||||
attempts += 1;
|
||||
requestDetailScrollReset();
|
||||
setTimeout(attemptRestore, 50);
|
||||
}
|
||||
};
|
||||
attemptRestore();
|
||||
}, [canRestoreFromDetailPanel, dispatch, requestDetailScrollReset]);
|
||||
|
||||
const restoreViaRef = useCallback(() => {
|
||||
restoreWhenScrollZero();
|
||||
}, [restoreWhenScrollZero]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
@@ -1641,7 +1667,9 @@ const MediaPanel = React.forwardRef(
|
||||
setModalStyle({});
|
||||
setModalScale(1);
|
||||
} else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
|
||||
console.log('[MediaPanel] Condition 3: Playing fullscreen video');
|
||||
console.log(
|
||||
'[MediaPanel] Condition 3: Playing fullscreen video - enforcing 1920x1080 rendering'
|
||||
);
|
||||
if (videoPlayer.current?.getMediaState()?.paused) {
|
||||
videoPlayer.current.play();
|
||||
}
|
||||
@@ -1652,6 +1680,14 @@ const MediaPanel = React.forwardRef(
|
||||
}
|
||||
}, [panelInfo, isOnTop, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!panelInfo.modal) {
|
||||
console.log('[MediaPanel] modal=false - resetting inline styles for fullscreen');
|
||||
setModalStyle({});
|
||||
setModalScale(1);
|
||||
}
|
||||
}, [panelInfo.modal]);
|
||||
|
||||
const smallestOffsetHourIndex = useMemo(() => {
|
||||
if (shopNowInfo) {
|
||||
const filteredVideos = shopNowInfo.filter((video) => video.offsetHour >= currentTime);
|
||||
@@ -2184,6 +2220,7 @@ const MediaPanel = React.forwardRef(
|
||||
const containerClassName = classNames(
|
||||
css.videoContainer,
|
||||
panelInfo.modal && css.modal,
|
||||
!panelInfo.modal && css.fullscreen,
|
||||
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
||||
// MediaPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
||||
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
||||
@@ -2192,12 +2229,23 @@ const MediaPanel = React.forwardRef(
|
||||
!captionEnable && css.hideSubtitle
|
||||
);
|
||||
|
||||
const panelStyleOverride = !panelInfo.modal
|
||||
? {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
top: 0,
|
||||
left: 0,
|
||||
position: 'fixed',
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<TPanel
|
||||
isTabActivated={false}
|
||||
{...props}
|
||||
className={containerClassName}
|
||||
handleCancel={handleClickBack}
|
||||
style={panelStyleOverride}
|
||||
spotlightId={spotlightId}
|
||||
>
|
||||
<Container
|
||||
@@ -2230,6 +2278,7 @@ const MediaPanel = React.forwardRef(
|
||||
{isReadyToPlay && (
|
||||
<div className={css.videoFrame}>
|
||||
<VideoPlayer
|
||||
className={!panelInfo.modal ? 'fullscreen' : undefined}
|
||||
setApiProvider={getPlayer}
|
||||
disabled={panelInfo.modal}
|
||||
onEnded={onEnded}
|
||||
@@ -2239,7 +2288,17 @@ const MediaPanel = React.forwardRef(
|
||||
spotlightDisabled={panelInfo.modal}
|
||||
isYoutube={isYoutube}
|
||||
src={currentPlayingUrl}
|
||||
style={panelInfo.modal ? modalStyle : {}}
|
||||
style={
|
||||
panelInfo.modal
|
||||
? modalStyle
|
||||
: {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}
|
||||
}
|
||||
modalScale={panelInfo.modal ? modalScale : 1}
|
||||
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
||||
spotlightId={
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
background-color: @videoBackgroundColor;
|
||||
z-index: 21;
|
||||
outline: none;
|
||||
inset: 0;
|
||||
|
||||
&:focus-visible,
|
||||
&:focus {
|
||||
@@ -18,6 +19,19 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&.fullscreen .videoFrame {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
&.fullscreen .videoFrame video,
|
||||
&.fullscreen .videoFrame iframe {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.videoFrame:focus-within {
|
||||
outline: 2px solid #c70850;
|
||||
border-radius: 6px;
|
||||
@@ -134,6 +148,18 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.fullscreen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: auto;
|
||||
z-index: 21;
|
||||
}
|
||||
&.fullscreen .media {
|
||||
width: 1920px !important;
|
||||
height: 100vh !important;
|
||||
}
|
||||
&.hideSubtitle {
|
||||
video::cue {
|
||||
visibility: hidden;
|
||||
|
||||
@@ -322,12 +322,14 @@ function PlayerOverlayContents({
|
||||
|
||||
<h2 className={css.patnerName}>{partnerName}</h2>
|
||||
|
||||
{!panelInfo?.modal && (
|
||||
<Marquee
|
||||
className={classNames(css.title, videoVerticalVisible && css.videoVerticalMarquee)}
|
||||
marqueeOn="render"
|
||||
>
|
||||
{showName}
|
||||
</Marquee>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
{type === 'VOD' && disclaimer && (
|
||||
|
||||
Reference in New Issue
Block a user