[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) => {
|
export const restoreModalMedia = () => (dispatch, getState) => {
|
||||||
const panels = getState().panels.panels;
|
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]] ========== Called ==========');
|
||||||
// console.log('[Restore] Total panels:', panels.length);
|
// console.log('[Restore] Total panels:', panels.length);
|
||||||
// console.log(
|
// console.log(
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
@import "~@enact/sandstone/styles/skin.less";
|
@import "~@enact/sandstone/styles/skin.less";
|
||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
@import "../../style/CommonStyle.module.less";
|
@import "../../style/CommonStyle.module.less";
|
||||||
|
.fullscreen .videoPlayer,
|
||||||
.videoPlayer {
|
.videoPlayer {
|
||||||
// Set by counting the IconButtons inside the side components.
|
// Set by counting the IconButtons inside the side components.
|
||||||
--liftDistance: 0px;
|
--liftDistance: 0px;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
margin: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
:focus {
|
:focus {
|
||||||
@@ -19,10 +21,17 @@
|
|||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
height: calc(100% - 4px);
|
height: 100%;
|
||||||
width: calc(100% - 4px);
|
width: 100%;
|
||||||
background: #000;
|
background: #000;
|
||||||
|
max-width: none;
|
||||||
|
max-height: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media {
|
.media {
|
||||||
@@ -48,6 +57,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
--media-width: 100vw;
|
||||||
|
--media-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen .videoPlayer .media {
|
||||||
|
--media-width: 100vw;
|
||||||
|
--media-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.preloadVideo {
|
.preloadVideo {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -537,6 +537,29 @@ export default function ProductAllSection({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const scrollContainerRef = useRef(null);
|
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 productDetailRef = useRef(null); //높이값 변경때문
|
||||||
const descriptionRef = useRef(null);
|
const descriptionRef = useRef(null);
|
||||||
const reviewRef = useRef(null);
|
const reviewRef = useRef(null);
|
||||||
@@ -666,6 +689,9 @@ export default function ProductAllSection({
|
|||||||
const handleScroll = useCallback(
|
const handleScroll = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
const currentScrollTop = e.scrollTop;
|
const currentScrollTop = e.scrollTop;
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.detailPanelScrollTop = Math.max(0, currentScrollTop);
|
||||||
|
}
|
||||||
const prevScrollTop = prevScrollTopRef.current;
|
const prevScrollTop = prevScrollTopRef.current;
|
||||||
|
|
||||||
scrollPositionRef.current = currentScrollTop;
|
scrollPositionRef.current = currentScrollTop;
|
||||||
|
|||||||
@@ -913,6 +913,7 @@ const MediaPanel = React.forwardRef(
|
|||||||
if (!panelInfo.modal) {
|
if (!panelInfo.modal) {
|
||||||
dispatch(PanelActions.popPanel());
|
dispatch(PanelActions.popPanel());
|
||||||
dispatch(changeAppStatus({ cursorVisible: false }));
|
dispatch(changeAppStatus({ cursorVisible: false }));
|
||||||
|
document?.dispatchEvent?.(new CustomEvent('detailpanel-scroll-reset'));
|
||||||
|
|
||||||
//딮링크로 플레이어 진입 후 이전버튼 클릭시
|
//딮링크로 플레이어 진입 후 이전버튼 클릭시
|
||||||
if (panels.length === 1) {
|
if (panels.length === 1) {
|
||||||
@@ -1120,9 +1121,34 @@ const MediaPanel = React.forwardRef(
|
|||||||
dispatch(minimizeModalMedia());
|
dispatch(minimizeModalMedia());
|
||||||
}, [dispatch]);
|
}, [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(restoreModalMedia());
|
||||||
}, [dispatch]);
|
} else if (attempts < 5) {
|
||||||
|
attempts += 1;
|
||||||
|
requestDetailScrollReset();
|
||||||
|
setTimeout(attemptRestore, 50);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
attemptRestore();
|
||||||
|
}, [canRestoreFromDetailPanel, dispatch, requestDetailScrollReset]);
|
||||||
|
|
||||||
|
const restoreViaRef = useCallback(() => {
|
||||||
|
restoreWhenScrollZero();
|
||||||
|
}, [restoreWhenScrollZero]);
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
ref,
|
ref,
|
||||||
@@ -1641,7 +1667,9 @@ const MediaPanel = React.forwardRef(
|
|||||||
setModalStyle({});
|
setModalStyle({});
|
||||||
setModalScale(1);
|
setModalScale(1);
|
||||||
} else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
|
} 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) {
|
if (videoPlayer.current?.getMediaState()?.paused) {
|
||||||
videoPlayer.current.play();
|
videoPlayer.current.play();
|
||||||
}
|
}
|
||||||
@@ -1652,6 +1680,14 @@ const MediaPanel = React.forwardRef(
|
|||||||
}
|
}
|
||||||
}, [panelInfo, isOnTop, dispatch]);
|
}, [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(() => {
|
const smallestOffsetHourIndex = useMemo(() => {
|
||||||
if (shopNowInfo) {
|
if (shopNowInfo) {
|
||||||
const filteredVideos = shopNowInfo.filter((video) => video.offsetHour >= currentTime);
|
const filteredVideos = shopNowInfo.filter((video) => video.offsetHour >= currentTime);
|
||||||
@@ -2184,6 +2220,7 @@ const MediaPanel = React.forwardRef(
|
|||||||
const containerClassName = classNames(
|
const containerClassName = classNames(
|
||||||
css.videoContainer,
|
css.videoContainer,
|
||||||
panelInfo.modal && css.modal,
|
panelInfo.modal && css.modal,
|
||||||
|
!panelInfo.modal && css.fullscreen,
|
||||||
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
||||||
// MediaPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
// MediaPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
||||||
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
||||||
@@ -2192,12 +2229,23 @@ const MediaPanel = React.forwardRef(
|
|||||||
!captionEnable && css.hideSubtitle
|
!captionEnable && css.hideSubtitle
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const panelStyleOverride = !panelInfo.modal
|
||||||
|
? {
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
position: 'fixed',
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TPanel
|
<TPanel
|
||||||
isTabActivated={false}
|
isTabActivated={false}
|
||||||
{...props}
|
{...props}
|
||||||
className={containerClassName}
|
className={containerClassName}
|
||||||
handleCancel={handleClickBack}
|
handleCancel={handleClickBack}
|
||||||
|
style={panelStyleOverride}
|
||||||
spotlightId={spotlightId}
|
spotlightId={spotlightId}
|
||||||
>
|
>
|
||||||
<Container
|
<Container
|
||||||
@@ -2230,6 +2278,7 @@ const MediaPanel = React.forwardRef(
|
|||||||
{isReadyToPlay && (
|
{isReadyToPlay && (
|
||||||
<div className={css.videoFrame}>
|
<div className={css.videoFrame}>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
|
className={!panelInfo.modal ? 'fullscreen' : undefined}
|
||||||
setApiProvider={getPlayer}
|
setApiProvider={getPlayer}
|
||||||
disabled={panelInfo.modal}
|
disabled={panelInfo.modal}
|
||||||
onEnded={onEnded}
|
onEnded={onEnded}
|
||||||
@@ -2239,7 +2288,17 @@ const MediaPanel = React.forwardRef(
|
|||||||
spotlightDisabled={panelInfo.modal}
|
spotlightDisabled={panelInfo.modal}
|
||||||
isYoutube={isYoutube}
|
isYoutube={isYoutube}
|
||||||
src={currentPlayingUrl}
|
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}
|
modalScale={panelInfo.modal ? modalScale : 1}
|
||||||
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
||||||
spotlightId={
|
spotlightId={
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
@videoBackgroundColor: black;
|
@videoBackgroundColor: black;
|
||||||
.videoContainer {
|
.videoContainer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: @videoBackgroundColor;
|
background-color: @videoBackgroundColor;
|
||||||
z-index: 21;
|
z-index: 21;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
inset: 0;
|
||||||
|
|
||||||
&:focus-visible,
|
&:focus-visible,
|
||||||
&:focus {
|
&:focus {
|
||||||
@@ -18,6 +19,19 @@
|
|||||||
box-sizing: border-box;
|
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 {
|
.videoFrame:focus-within {
|
||||||
outline: 2px solid #c70850;
|
outline: 2px solid #c70850;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -134,6 +148,18 @@
|
|||||||
display: none;
|
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 {
|
&.hideSubtitle {
|
||||||
video::cue {
|
video::cue {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|||||||
@@ -322,12 +322,14 @@ function PlayerOverlayContents({
|
|||||||
|
|
||||||
<h2 className={css.patnerName}>{partnerName}</h2>
|
<h2 className={css.patnerName}>{partnerName}</h2>
|
||||||
|
|
||||||
|
{!panelInfo?.modal && (
|
||||||
<Marquee
|
<Marquee
|
||||||
className={classNames(css.title, videoVerticalVisible && css.videoVerticalMarquee)}
|
className={classNames(css.title, videoVerticalVisible && css.videoVerticalMarquee)}
|
||||||
marqueeOn="render"
|
marqueeOn="render"
|
||||||
>
|
>
|
||||||
{showName}
|
{showName}
|
||||||
</Marquee>
|
</Marquee>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
{type === 'VOD' && disclaimer && (
|
{type === 'VOD' && disclaimer && (
|
||||||
|
|||||||
Reference in New Issue
Block a user