[DetailPanel] themeIndicator focus

This commit is contained in:
고동영
2024-05-09 16:33:43 +09:00
parent 5388118ca3
commit a8fb870f63
8 changed files with 129 additions and 262 deletions

View File

@@ -1,33 +1,26 @@
import React, {
useCallback,
useEffect,
useState,
} from 'react';
import React, { useCallback, useEffect, useState } from "react";
import {
useDispatch,
useSelector,
} from 'react-redux';
import { useDispatch, useSelector } from "react-redux";
import {
clearThemeDetail,
getThemeCurationDetailInfo,
getThemeHotelDetailInfo,
} from '../../actions/homeActions';
import { getMainCategoryDetail } from '../../actions/mainActions';
import { popPanel } from '../../actions/panelActions';
import { finishVideoPreview } from '../../actions/playActions';
import { clearProductDetail } from '../../actions/productActions';
import TBody from '../../components/TBody/TBody';
import THeader from '../../components/THeader/THeader';
import TPanel from '../../components/TPanel/TPanel';
import { panel_names } from '../../utils/Config';
import css from './DetailPanel.module.less';
import GroupProduct from './GroupProduct/GroupProduct';
import SingleProduct from './SingleProduct/SingleProduct';
import ThemeProduct from './ThemeProduct/ThemeProduct';
import UnableProduct from './UnableProduct/UnableProduct';
import YouMayLike from './YouMayLike/YouMayLike';
} from "../../actions/homeActions";
import { getMainCategoryDetail } from "../../actions/mainActions";
import { popPanel } from "../../actions/panelActions";
import { finishVideoPreview } from "../../actions/playActions";
import { clearProductDetail } from "../../actions/productActions";
import TBody from "../../components/TBody/TBody";
import THeader from "../../components/THeader/THeader";
import TPanel from "../../components/TPanel/TPanel";
import { panel_names } from "../../utils/Config";
import css from "./DetailPanel.module.less";
import GroupProduct from "./GroupProduct/GroupProduct";
import SingleProduct from "./SingleProduct/SingleProduct";
import ThemeProduct from "./ThemeProduct/ThemeProduct";
import UnableProduct from "./UnableProduct/UnableProduct";
import YouMayLike from "./YouMayLike/YouMayLike";
export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
const [lgCatCd, setLgCatCd] = useState("");
@@ -48,6 +41,8 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
const { prdtId, patnrId, curationId, type, bgImgNo } = panelInfo;
useEffect(() => {
console.log("#panelInfo ", panelInfo);
if (type === "hotel") {
dispatch(
getThemeHotelDetailInfo({

View File

@@ -33,6 +33,6 @@
position: relative;
display: flex;
justify-content: space-between;
height: 100%;
padding-left: 120px;
}

View File

@@ -46,7 +46,8 @@ export default function ShowOption({
<>
<Container className={css.indicatorContainer}>
<ThemeIndicator
productInfo={productInfo}
themeProductInfos={productInfo}
themeProductInfo={productInfo[selectedIndex]}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
thumbnailUrls={productInfo[selectedIndex]?.imgUrls600}

View File

@@ -1,40 +1,26 @@
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import Image from '@enact/sandstone/Image';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import Image from "@enact/sandstone/Image";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import playImg from '../../../../../assets/images/btn/btn-play-thumb-nor.png';
import defaultImage
from '../../../../../assets/images/img-thumb-empty-144@3x.png';
import {
pushPanel,
updatePanel,
} from '../../../../actions/panelActions';
import playImg from "../../../../../assets/images/btn/btn-play-thumb-nor.png";
import defaultImage from "../../../../../assets/images/img-thumb-empty-144@3x.png";
import { pushPanel, updatePanel } from "../../../../actions/panelActions";
import {
finishVideoPreview,
startVideoPlayer,
} from '../../../../actions/playActions';
import TVirtualGridList
from '../../../../components/TVirtualGridList/TVirtualGridList';
import usePriceInfo from '../../../../hooks/usePriceInfo';
import useScrollTo from '../../../../hooks/useScrollTo';
import { panel_names } from '../../../../utils/Config';
import { scaleW } from '../../../../utils/helperMethods';
import css from './Indicator.module.less';
} from "../../../../actions/playActions";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import usePriceInfo from "../../../../hooks/usePriceInfo";
import useScrollTo from "../../../../hooks/useScrollTo";
import { panel_names } from "../../../../utils/Config";
import { scaleW } from "../../../../utils/helperMethods";
import css from "./Indicator.module.less";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveld: true },
@@ -91,7 +77,6 @@ function Indicator({
}, [dispatch, productInfo, autoPlaying, focused, selectedIndex]);
const modalClassNameChange = useCallback(() => {
console.log("##################");
if (selectedIndex === 0) {
if (focused) {
return css.videoModal;
@@ -164,13 +149,8 @@ function Indicator({
}, [dispatch, productInfo, autoPlaying, canPlayVideo]);
const onSpotlightRight = useCallback(() => {
const timer = setTimeout(() => {
if (isOnTop) Spotlight.focus(`indicator-image-${selectedIndex}`);
});
return () => {
clearTimeout(timer);
};
}, [selectedIndex, isOnTop]);
Spotlight.focus(`indicator-image-${selectedIndex}`);
}, [selectedIndex]);
const onSpotlightLeft = useCallback(() => {
if (isOnTop) Spotlight.focus("spotlightId_backBtn");

View File

@@ -16,36 +16,8 @@
.size(@w: 560px, @h: 560px);
margin-right: 10px;
position: relative;
.player {
.size(@w: 560px, @h: 560px);
margin: 0 10px 10px 0;
position: relative;
background-color: #222;
// &:focus {
// &::before {
// z-index: 2;
// .focused(@boxShadow: 22px, @borderRadius:0px);
// }
// }
&:after {
.size(@w: 560px, @h: 200px);
position: absolute;
left: 0;
bottom: 0;
content: "";
background: linear-gradient(
to top,
rgba(255, 255, 255, 1),
transparent
);
opacity: 0.2;
}
}
.disclaimerContainer {
// .size(@w: 524px , @h: 74px);
width: 524px;
padding: 12px;
border-radius: 5px;
@@ -92,11 +64,6 @@
position: relative;
z-index: 1;
}
&:focus {
&::after {
.focused(@boxShadow: 22px, @borderRadius:0px);
}
}
&.soldout {
&:before {
@@ -109,53 +76,6 @@
opacity: 0.7;
}
}
.thumbnailIndicator {
margin-top: auto;
position: relative;
margin-bottom: 30px;
z-index: 1;
.prevButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @right: 70px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
}
}
.nextButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @left: 70px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-next-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
}
}
> span {
// .size(@w: 51px , @h: 21px);
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.7);
color: #222222;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
}
}
}
.promotionImage {

View File

@@ -1,31 +1,20 @@
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import Image from '@enact/sandstone/Image';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import Image from "@enact/sandstone/Image";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import defaultImage
from '../../../../../assets/images/img-thumb-empty-144@3x.png';
import { startVideoPlayer } from '../../../../actions/playActions';
import TVirtualGridList
from '../../../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../../../hooks/useScrollTo';
import { panel_names } from '../../../../utils/Config';
import { scaleW } from '../../../../utils/helperMethods';
import css from './ThemeIndicator.module.less';
import defaultImage from "../../../../../assets/images/img-thumb-empty-144@3x.png";
import { startVideoPlayer } from "../../../../actions/playActions";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../hooks/useScrollTo";
import { panel_names } from "../../../../utils/Config";
import { scaleW } from "../../../../utils/helperMethods";
import css from "./ThemeIndicator.module.less";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveld: true },
@@ -39,7 +28,8 @@ const IMAGE_WIDTH = scaleW(152);
export default function ThemeIndicator({
selectedIndex,
setSelectedIndex,
productInfo,
themeProductInfos,
themeProductInfo,
thumbnailUrls,
isSpotlight,
soldoutFlag,
@@ -50,7 +40,7 @@ export default function ThemeIndicator({
const [focused, setFocused] = useState(false);
const [imageSelectedIndex, setImageSelectedIndex] = useState(0);
const [autoPlaying, setAutoPlaying] = useState(
!launchedFromPlayer && productInfo[selectedIndex]?.prdtMediaUrl
!launchedFromPlayer && themeProductInfo?.prdtMediaUrl
);
const { cursorVisible } = useSelector((state) => state.common.appStatus);
const panels = useSelector((state) => state.panels.panels);
@@ -61,10 +51,9 @@ export default function ThemeIndicator({
const getProductMediaUrlStatus = useCallback(() => {
return (
productInfo[selectedIndex]?.prdtMediaUrl &&
productInfo[selectedIndex]?.prdtMediaUrl !== null
themeProductInfo?.prdtMediaUrl && themeProductInfo?.prdtMediaUrl !== null
);
}, [productInfo, selectedIndex]);
}, [themeProductInfo]);
useEffect(() => {
if (thumbnailUrls) {
@@ -79,10 +68,8 @@ export default function ThemeIndicator({
}, [thumbnailUrls, imageSelectedIndex]);
useEffect(() => {
setAutoPlaying(
!launchedFromPlayer && productInfo[selectedIndex]?.prdtMediaUrl
);
}, [launchedFromPlayer, productInfo, selectedIndex]);
setAutoPlaying(!launchedFromPlayer && themeProductInfo?.prdtMediaUrl);
}, [launchedFromPlayer, themeProductInfo]);
useEffect(() => {
if (
topPanel &&
@@ -92,15 +79,15 @@ export default function ThemeIndicator({
return; //prevent
}
console.log("#focused", focused);
if (autoPlaying && productInfo[selectedIndex]?.prdtMediaUrl) {
if (autoPlaying && themeProductInfo?.prdtMediaUrl) {
//auto play.
dispatch(
startVideoPlayer({
showUrl: productInfo[selectedIndex]?.prdtMediaUrl,
showNm: productInfo[selectedIndex]?.prdtNm,
patnrNm: productInfo[selectedIndex]?.patncNm,
patncLogoPath: productInfo[selectedIndex]?.patncLogoPath,
orderPhnNo: productInfo[selectedIndex]?.orderPhnNo,
showUrl: themeProductInfo?.prdtMediaUrl,
showNm: themeProductInfo?.prdtNm,
patnrNm: themeProductInfo?.patncNm,
patncLogoPath: themeProductInfo?.patncLogoPath,
orderPhnNo: themeProductInfo?.orderPhnNo,
shptmBanrTpNm: "MEDIA",
modal: true,
modalContainerId: "indicator_videoContainer", //to calc width, height, left, top
@@ -109,15 +96,7 @@ export default function ThemeIndicator({
})
);
}
}, [
dispatch,
productInfo,
selectedIndex,
autoPlaying,
canPlayVideo,
imageSelectedIndex,
focused,
]);
}, [dispatch, themeProductInfo, autoPlaying, imageSelectedIndex, focused]);
const modalClassNameChange = useCallback(() => {
if (imageSelectedIndex === 0) {
@@ -128,28 +107,29 @@ export default function ThemeIndicator({
} else {
return css.videoModalHide;
}
}, [imageSelectedIndex, canPlayVideo, focused]);
}, [imageSelectedIndex, focused]);
const onFocus = useCallback(() => {
if (
autoPlaying &&
canPlayVideo &&
productInfo[selectedIndex]?.prdtMediaUrl &&
themeProductInfo?.prdtMediaUrl &&
imageSelectedIndex === 0
) {
console.log("#onFocus");
setFocused(true);
}
}, [autoPlaying, productInfo, imageSelectedIndex, canPlayVideo]);
}, [autoPlaying, themeProductInfo, imageSelectedIndex]);
const onBlur = useCallback(() => {
if (
autoPlaying &&
productInfo[selectedIndex]?.prdtMediaUrl &&
themeProductInfo?.prdtMediaUrl &&
imageSelectedIndex === 0
) {
console.log("#onBlur");
setFocused(false);
}
}, [autoPlaying, productInfo, imageSelectedIndex]);
}, [autoPlaying, themeProductInfo, imageSelectedIndex]);
const handlePrevClick = (e) => {
if (imageSelectedIndex > 0) {
@@ -179,13 +159,13 @@ export default function ThemeIndicator({
}
setSelectedIndex((prev) => prev - 1);
setImageSelectedIndex(0);
if (productInfo.length - 1 !== selectedIndex) {
if (themeProductInfos.length - 1 !== selectedIndex) {
scrollTop({ y: imagePosition - IMAGE_WIDTH, animate: true });
}
}, [selectedIndex]);
const handleDownClick = useCallback(() => {
if (productInfo.length - 1 === selectedIndex) {
if (themeProductInfos.length - 1 === selectedIndex) {
return;
}
@@ -201,8 +181,8 @@ export default function ThemeIndicator({
const renderItem = useCallback(
({ index, ...rest }) => {
const thumbnailUrl = productInfo[index].thumbnailUrl;
const hotelImgUrl = productInfo[index].hotelImgUrl;
const thumbnailUrl = themeProductInfos[index].thumbnailUrl;
const hotelImgUrl = themeProductInfos[index].hotelImgUrl;
const handleItemClick = () => {
setSelectedIndex(index);
@@ -231,36 +211,36 @@ export default function ThemeIndicator({
</>
);
},
[productInfo, selectedIndex]
[themeProductInfos, selectedIndex]
);
const canPlayVideo = useMemo(() => {
return productInfo[selectedIndex]?.prdtMediaUrl && imageSelectedIndex === 0;
}, [productInfo, selectedIndex, imageSelectedIndex]);
return themeProductInfo?.prdtMediaUrl && imageSelectedIndex === 0;
}, [themeProductInfo, imageSelectedIndex]);
useEffect(() => {
if (soldoutFlag) {
Spotlight.focus("spotlightId_backBtn");
return;
}
if (productInfo && productInfo.length > 0 && isSpotlight) {
if (themeProductInfos && themeProductInfos.length > 0 && isSpotlight) {
Spotlight.focus("IndicatorGridlistContainer");
Spotlight.focus("indicator-image-0");
}
}, [productInfo, isSpotlight]);
}, [themeProductInfos, isSpotlight]);
const handleVideoOnClick = useCallback(() => {
if (canPlayVideo) {
dispatch(
startVideoPlayer({
showUrl: productInfo[selectedIndex]?.prdtMediaUrl,
showNm: productInfo[selectedIndex]?.prdtNm,
patnrNm: productInfo[selectedIndex]?.patncNm,
patncLogoPath: productInfo[selectedIndex]?.patncLogoPath,
orderPhnNo: productInfo[selectedIndex]?.orderPhnNo,
showUrl: themeProductInfo.prdtMediaUrl,
showNm: themeProductInfo.prdtNm,
patnrNm: themeProductInfo.patncNm,
patncLogoPath: themeProductInfo.patncLogoPath,
orderPhnNo: themeProductInfo.orderPhnNo,
shptmBanrTpNm: "MEDIA",
modal: false,
modalContainerId: null,
modal: !autoPlaying,
modalContainerId: "indicator_videoContainer",
modalClassName: css.videoModal,
spotlightDisable: false,
})
@@ -269,8 +249,9 @@ export default function ThemeIndicator({
setAutoPlaying(true);
}
}
}, [dispatch, productInfo, selectedIndex, canPlayVideo]);
}, [dispatch, themeProductInfo, canPlayVideo, autoPlaying]);
console.log("#spotlight ", Spotlight.getCurrent());
const renderThumbnail = useCallback(() => {
const hasMediaUrl = getProductMediaUrlStatus();
@@ -286,11 +267,10 @@ export default function ThemeIndicator({
onBlur={onBlur}
>
<Image
spotlightDisabled
className={css.image}
className={classNames(css.image, soldoutFlag && css.soldout)}
src={autoPlaying && canPlayVideo ? "" : selectedImage}
alt=""
></Image>
/>
</SpottableComponent>
<div className={css.thumbnailIndicator}>
{soldoutFlag ? (
@@ -327,8 +307,6 @@ export default function ThemeIndicator({
)}
onClick={handleNextClick}
spotlightDisabled={imageLength === imageSelectedIndex}
spotlightId="thumbnailNextButton"
onFocus={() => onFocus(false)}
/>
</>
)}
@@ -336,9 +314,7 @@ export default function ThemeIndicator({
</div>
);
}, [
productInfo,
imageSelectedIndex,
selectedIndex,
selectedImage,
soldoutFlag,
autoPlaying,
@@ -361,11 +337,11 @@ export default function ThemeIndicator({
className={css.tVirtualGridListContainer}
spotlightId="IndicatorGridlistContainer"
>
{productInfo && productInfo.length > 0 && (
{themeProductInfos && themeProductInfos.length > 0 && (
<TVirtualGridList
cbScrollTo={getScrollTo}
className={css.tVirtualGridList}
dataSize={productInfo.length}
dataSize={themeProductInfos.length}
itemWidth={144}
itemHeight={144}
spacing={8}
@@ -378,7 +354,7 @@ export default function ThemeIndicator({
spotlightDisabled={!cursorVisible}
className={classNames(
css.downButton,
productInfo.length - 1 === selectedIndex && css.disable
themeProductInfos.length - 1 === selectedIndex && css.disable
)}
/>
</div>

View File

@@ -11,6 +11,8 @@
.thumbnailContainer {
.size(@w: 560px, @h: 560px);
margin-right: 10px;
position: relative;
.disclaimerContainer {
// .size(@w: 524px , @h: 74px);
width: 524px;
@@ -38,7 +40,9 @@
.thumbnailIndicator {
margin-top: auto;
position: relative;
position: absolute;
bottom: -5px;
left: 260px;
margin-bottom: 30px;
z-index: 1;
@@ -92,7 +96,7 @@
.thumbnail {
.size(@w: 560px, @h: 560px);
border: solid 1px #dadada;
box-shadow: 0 0 0 1px #dadada inset;
background-color: #fff;
margin: 0 10px 10px 0;
position: relative;
@@ -107,11 +111,6 @@
top: 0;
}
// &:focus {
// &::after {
// .focused(@boxShadow: 22px, @borderRadius:0px);
// }
// }
&.soldout {
&:before {
.size(@w: 560px , @h: 560px);

View File

@@ -4,46 +4,41 @@ import React, {
useMemo,
useRef,
useState,
} from 'react';
} from "react";
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import { Job } from '@enact/core/util';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import dummyVtt from '../../../assets/mock/video.vtt';
import * as CommonActions from '../../actions/commonActions';
import dummyVtt from "../../../assets/mock/video.vtt";
import * as CommonActions from "../../actions/commonActions";
import {
getHomeFullVideoInfo,
getMainCategoryShowDetail,
getMainLiveShow,
getMainLiveShowNowProduct,
} from '../../actions/mainActions';
import * as PanelActions from '../../actions/panelActions';
import { updatePanel } from '../../actions/panelActions';
} from "../../actions/mainActions";
import * as PanelActions from "../../actions/panelActions";
import { updatePanel } from "../../actions/panelActions";
import {
getChatLog,
getSubTitle,
startVideoPlayer,
} from '../../actions/playActions';
import TPanel from '../../components/TPanel/TPanel';
import VideoOverlayWithPhoneNumber
from '../../components/VideoOverlayWithPhoneNumber/VideoOverlayWithPhoneNumber';
import { VideoPlayer } from '../../components/VideoPlayer/VideoPlayer';
import * as Config from '../../utils/Config';
import { panel_names } from '../../utils/Config';
import { $L } from '../../utils/helperMethods';
import PlayerOverlayChat from './PlayerOverlay/PlayerOverlayChat';
import PlayerOverlayQRCode from './PlayerOverlay/PlayerOverlayQRCode';
import css from './PlayerPanel.module.less';
import PlayerTabButton from './PlayerTabContents/TabButton/PlayerTabButton';
import TabContainer from './PlayerTabContents/TabContaienr';
} from "../../actions/playActions";
import TPanel from "../../components/TPanel/TPanel";
import VideoOverlayWithPhoneNumber from "../../components/VideoOverlayWithPhoneNumber/VideoOverlayWithPhoneNumber";
import { VideoPlayer } from "../../components/VideoPlayer/VideoPlayer";
import * as Config from "../../utils/Config";
import { panel_names } from "../../utils/Config";
import { $L } from "../../utils/helperMethods";
import PlayerOverlayChat from "./PlayerOverlay/PlayerOverlayChat";
import PlayerOverlayQRCode from "./PlayerOverlay/PlayerOverlayQRCode";
import css from "./PlayerPanel.module.less";
import PlayerTabButton from "./PlayerTabContents/TabButton/PlayerTabButton";
import TabContainer from "./PlayerTabContents/TabContaienr";
const unableToPlay = new Job((callback) => {
callback();
@@ -86,6 +81,7 @@ const PlayerPanel = ({
const liveShowInfos = useSelector((state) => state.main.liveShowInfos);
const vodSubtitleData = useSelector((state) => state.play.subTitleBlobs);
console.log("#shopNowInfo", showNowInfos);
const onClickBack = useCallback(
(ev) => {
//modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.