[251112] feat: ProductVideoV2 fullScreen Return Focus

🕐 커밋 시간: 2025. 11. 12. 18:16:27

📊 변경 통계:
  • 총 파일: 4개
  • 추가: +64줄
  • 삭제: -49줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.module.less

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx (javascript):
     Deleted: Spottable()
  📄 com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.module.less (unknown):
     Added: imgElement()
     Deleted: imgElement()

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
This commit is contained in:
2025-11-12 18:16:29 +09:00
parent b17aa89c28
commit ec4b7736f8
4 changed files with 81 additions and 85 deletions

View File

@@ -534,23 +534,7 @@
}
// FavoriteBtn 컴포넌트에 적용할 스타일
.favoriteBtn {
width: 60px !important;
height: 60px !important;
background: rgba(68, 68, 68, 0.5) !important;
border-radius: 6px !important;
border: none !important;
padding: 0 !important;
margin: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
&:focus {
background: @PRIMARY_COLOR_RED !important;
// outline은 사용하지 않음
}
}
// .favoriteBtn 클래스는 FavoriteBtn 컴포넌트 내부 스타일로 대체됨
// 액션 버튼들 (actionButtons 참고)
.actionButtonsWrapper {

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState, useRef } from 'react';
import ReactDOM from 'react-dom';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import Spottable from '@enact/spotlight/Spottable';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import CustomImage from '../../../../components/CustomImage/CustomImage';
@@ -85,6 +86,7 @@ export function ProductVideoV2({
const normalContainerRef = useRef(null);
const fullscreenContainerRef = useRef(null);
const videoPortalHostRef = useRef(null);
const prevFullscreenRef = useRef(isFullscreen);
const isFullscreenRef = useRef(isFullscreen);
useEffect(() => {
@@ -638,6 +640,22 @@ export function ProductVideoV2({
}
}, [isFullscreen, isPlaying]);
useEffect(() => {
const wasFullscreen = prevFullscreenRef.current;
if (wasFullscreen && !isFullscreen && isPlaying) {
const targetSpotlightId = 'product-video-v2-playing';
if (typeof window !== 'undefined') {
window.requestAnimationFrame(() => {
const focused = Spotlight.focus?.(targetSpotlightId);
if (!focused) {
containerRef.current?.focus?.();
}
});
}
}
prevFullscreenRef.current = isFullscreen;
}, [isFullscreen, isPlaying]);
// VideoContainer의 모든 클릭 감시 (Hooks는 early return 전에 정의되어야 함)
const handleVideoContainerClick = useCallback(
(e) => {

View File

@@ -1,26 +1,14 @@
import React, {
useCallback,
useEffect,
} from 'react';
import React, { useCallback, useEffect } from 'react';
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import Spottable from '@enact/spotlight/Spottable';
import {
setHidePopup,
setShowPopup,
} from '../../../actions/commonActions';
import { setHidePopup, setShowPopup } from '../../../actions/commonActions';
import { sendLogTotalRecommend } from '../../../actions/logActions';
import {
getMyFavoriteFlag,
setMainLikeCategory,
} from '../../../actions/mainActions';
import { getMyFavoriteFlag, setMainLikeCategory } from '../../../actions/mainActions';
import { deleteMyFavorite } from '../../../actions/myPageActions';
import TButton from '../../../components/TButton/TButton';
import TPopUp from '../../../components/TPopUp/TPopUp';
@@ -40,21 +28,19 @@ export default function FavoriteBtn({
kind,
}) {
const dispatch = useDispatch();
const { popupVisible, activePopup } = useSelector(
(state) => state.common.popup
);
const { popupVisible, activePopup } = useSelector((state) => state.common.popup);
const handleFavoriteClick = useCallback(() => {
dispatch(
sendLogTotalRecommend({
menu: logMenu,
buttonTitle: "FAVORITE",
buttonTitle: 'FAVORITE',
contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
messageId: Config.LOG_MESSAGE_ID.BUTTONCLICK,
})
);
switch (favoriteFlag) {
case "N":
case 'N':
dispatch(
setMainLikeCategory({
patnrId: selectedPatnrId,
@@ -63,10 +49,8 @@ export default function FavoriteBtn({
);
dispatch(setShowPopup(Config.ACTIVE_POPUP.favoritePopup));
break;
case "Y": {
const productList = [
{ patnrId: selectedPatnrId, prdtId: selectedPrdtId },
];
case 'Y': {
const productList = [{ patnrId: selectedPatnrId, prdtId: selectedPrdtId }];
const showList = [];
dispatch(deleteMyFavorite({ productList, showList }));
dispatch(setShowPopup(Config.ACTIVE_POPUP.favoritePopup));
@@ -77,25 +61,24 @@ export default function FavoriteBtn({
}, [dispatch, favoriteFlag, selectedPatnrId, selectedPrdtId, logMenu]);
const PopUpOnClick = useCallback(() => {
setTimeout(() => Spotlight.focus("favoriteBtn"));
setTimeout(() => Spotlight.focus('favoriteBtn'));
dispatch(setHidePopup());
onFavoriteFlagChanged(favoriteFlag === "Y" ? "N" : "Y");
onFavoriteFlagChanged(favoriteFlag === 'Y' ? 'N' : 'Y');
}, [dispatch, favoriteFlag, onFavoriteFlagChanged]);
return (
<SpottableDiv
className={classNames(
css.favorBtnContainer,
kind === "item_detail" ? css.smallSize : ""
)}
className={classNames(css.favorBtnContainer, kind === 'item_detail' ? css.smallSize : '')}
spotlightId="favoriteBtn"
>
<TButton
<div
className={classNames(
favoriteFlag === "N" ? css.favorBtn : css.favorUnableBtn
css.favoriteButton,
favoriteFlag === 'N' ? css.favorBtn : css.favorUnableBtn
)}
onClick={handleFavoriteClick}
ariaLabel="Register in Favorites"
spotlightId={"favoriteBtn"}
role="button"
aria-label="Register in Favorites"
/>
{activePopup === Config.ACTIVE_POPUP.favoritePopup && (
<TPopUp
@@ -103,12 +86,12 @@ export default function FavoriteBtn({
open={popupVisible}
onClose={PopUpOnClick}
hasButton
button1Text={$L("OK")}
button1Text={$L('OK')}
hasText
text={
favoriteFlag && favoriteFlag === "N"
? $L("Added to My Favorites List")
: $L("Removed from My Favorites List")
favoriteFlag && favoriteFlag === 'N'
? $L('Added to My Favorites List')
: $L('Removed from My Favorites List')
}
onClick={PopUpOnClick}
/>

View File

@@ -3,40 +3,51 @@
.favorBtnContainer {
height: 78px;
&.smallSize {
height: 60px;
.flex();
.flex();
.favorBtn {
background-color: rgba(68, 68, 68, 0.5) !important;
min-width: 60px;
height: 60px;
background-image: url(../../../../assets/images/icons/ic_heart_3x.png);
.imgElement(29px, 25px, center, center);
&:focus {
background-color: @PRIMARY_COLOR_RED !important; // 포커스시 빨간색 배경
// outline: 2px solid @PRIMARY_COLOR_RED !important;
background-image: url(../../../../assets/images/icons/ic_heart_3x.png);
}
.favoriteButton {
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
border: none;
transition: background-color 0.2s ease;
&.favorBtn {
min-width: 78px;
height: 78px;
background-image: url(../../../../assets/images/icons/ic-heart-nor@3x.png);
background-color: rgba(68, 68, 68, 0.5);
.imgElement(54px, 54px, center, center);
}
.favorUnableBtn {
min-width: 60px;
height: 60px;
background-color: rgba(68, 68, 68, 0.5); // 다른 버튼들과 동일한 배경색
&.favorUnableBtn {
min-width: 78px;
height: 78px;
background-image: url(../../../../assets/images/icons/ic-heart-sel@3x.png);
background-color: rgba(68, 68, 68, 0.5);
.imgElement(54px, 54px, center, center);
}
}
.flex();
.favorBtn {
min-width: 78px;
height: 78px;
background-image: url(../../../../assets/images/icons/ic-heart-nor@3x.png);
.imgElement(54px, 54px, center, center);
&:focus {
outline: none;
.favoriteButton {
background-color: @PRIMARY_COLOR_RED !important;
}
}
.favorUnableBtn {
min-width: 78px;
height: 78px;
background-image: url(../../../../assets/images/icons/ic-heart-sel@3x.png);
.imgElement(54px, 54px, center, center);
&.smallSize {
height: 60px;
.favoriteButton {
&.favorBtn,
&.favorUnableBtn {
min-width: 60px;
height: 60px;
background-color: rgba(68, 68, 68, 0.5);
}
}
}
}