[FeaturedBrandsPanel] LiveVideoCard 추가 및 반영

Detail Notes :

1. LiveVideoCard.jsx 추가 및 반영
This commit is contained in:
younghoon100.park
2024-01-30 18:01:22 +09:00
parent 696a5fec18
commit 645827ce54
6 changed files with 260 additions and 84 deletions

View File

@@ -2,9 +2,11 @@ import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import SectionTitle from "../../components/SectionTitle/SectionTitle";
import TPanel from "../../components/TPanel/TPanel"; import TPanel from "../../components/TPanel/TPanel";
import { getBrandLayoutInfo, getBrandLiveChannelInfo } from "../../features/brand/brandsSlice"; import {
getBrandLayoutInfo,
getBrandLiveChannelInfo,
} from "../../features/brand/brandsSlice";
import Banner from "./Banner/Banner"; import Banner from "./Banner/Banner";
import css from "./FeaturedBrandsPanel.module.less"; import css from "./FeaturedBrandsPanel.module.less";
import LiveChannels from "./LiveChannels/LiveChannels"; import LiveChannels from "./LiveChannels/LiveChannels";
@@ -19,9 +21,13 @@ export default function FeaturedBrandsPanel() {
const brandInfo = useSelector((state) => state.brand.brandInfoData.brandInfo); const brandInfo = useSelector((state) => state.brand.brandInfoData.brandInfo);
const brandTopImgInfo = useSelector((state) => state.brand.brandLayoutInfoData.brandTopImgInfo); const brandTopImgInfo = useSelector(
(state) => state.brand.brandLayoutInfoData.brandTopImgInfo
);
const brandChanInfo = useSelector((state) => state.brand.brandLiveChannelInfoData.brandChanInfo); const brandChanInfo = useSelector(
(state) => state.brand.brandLiveChannelInfoData.brandChanInfo
);
const brandChannelCnt = useSelector( const brandChannelCnt = useSelector(
(state) => state.brand.brandLiveChannelInfoData.brandChannelCnt (state) => state.brand.brandLiveChannelInfoData.brandChannelCnt
); );
@@ -56,12 +62,18 @@ export default function FeaturedBrandsPanel() {
)} )}
{selectedBrandInfo && ( {selectedBrandInfo && (
<Banner selectedBrandInfo={selectedBrandInfo} brandTopImgInfo={brandTopImgInfo} /> <Banner
selectedBrandInfo={selectedBrandInfo}
brandTopImgInfo={brandTopImgInfo}
/>
)} )}
<div className={css.sectionContainer}> <div className={css.sectionContainer}>
{brandChanInfo && ( {brandChanInfo && (
<LiveChannels brandChanInfo={brandChanInfo[0]} brandChannelCnt={brandChannelCnt} /> <LiveChannels
brandChanInfo={brandChanInfo[0]}
brandChannelCnt={brandChannelCnt}
/>
)} )}
</div> </div>
</TPanel> </TPanel>

View File

@@ -1,16 +1,13 @@
import Scroller from "@enact/sandstone/Scroller"; import Scroller from "@enact/sandstone/Scroller";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import SectionTitle from "../../../components/SectionTitle/SectionTitle"; import SectionTitle from "../../../components/SectionTitle/SectionTitle";
import TItemCard from "../../../components/TItemCard/TItemCard"; import TItemCard from "../../../components/TItemCard/TItemCard";
import { $L } from "../../../utils/helperMethods"; import { $L } from "../../../utils/helperMethods";
import LiveVideoCard from "../LiveVideoCard/LiveVideoCard";
import css from "./LiveChannels.module.less"; import css from "./LiveChannels.module.less";
const LIVE_CHANNELS_STRING = "LIVE CHANNELS"; const LIVE_CHANNELS_STRING = "LIVE CHANNELS";
const SpottableComponent = Spottable("div");
export default function LiveChannels({ brandChanInfo, brandChannelCnt }) { export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
const { const {
alamDispFlag, alamDispFlag,
@@ -60,12 +57,20 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
// } // }
return ( return (
<>
{brandChannelCnt > 0 && (
<div className={css.container}> <div className={css.container}>
<SectionTitle title={$L(LIVE_CHANNELS_STRING)} /> <SectionTitle title={$L(LIVE_CHANNELS_STRING)} />
{brandChannelCnt === 1 && ( {brandChannelCnt === 1 && (
<div> <div>
<SpottableComponent>Live 영상</SpottableComponent> <LiveVideoCard
endTime={endDt}
liveChannelCount={brandChannelCnt}
startTime={strtDt}
thumbnailSource={thumbnailImgPath}
title={showNm}
/>
<Scroller <Scroller
className={css.scroller} className={css.scroller}
direction="vertical" direction="vertical"
@@ -76,7 +81,13 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
<ul> <ul>
{brandProductInfo && {brandProductInfo &&
brandProductInfo.map( brandProductInfo.map(
({ prdtId, prdtImgUrl, prdtNm, priceInfo, soldoutFlag }) => { ({
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
soldoutFlag,
}) => {
return ( return (
<TItemCard <TItemCard
key={prdtId} key={prdtId}
@@ -102,7 +113,13 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
<ul> <ul>
{brandProductInfo && {brandProductInfo &&
brandProductInfo.map( brandProductInfo.map(
({ prdtId, prdtImgUrl, prdtNm, priceInfo, soldoutFlag }) => { ({
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
soldoutFlag,
}) => {
return ( return (
<TItemCard <TItemCard
key={prdtId} key={prdtId}
@@ -120,5 +137,7 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
</div> </div>
)} )}
</div> </div>
)}
</>
); );
} }

View File

@@ -12,23 +12,6 @@
display: flex; display: flex;
width: @globalWidth; width: @globalWidth;
// live container
> div:nth-child(1) {
.size(@w: 1002px, @h: 564px);
margin-right: 18px;
background-color: gainsboro;
border: solid 1px transparent;
border-radius: 12px;
/* focused */
&:focus-within {
// live container
border: solid 1px @PRIMARY_COLOR_RED;
box-shadow: inset 0 0 0 4px @PRIMARY_COLOR_RED,
0 0 50px 0 rgba(0, 0, 0, 0.5);
}
}
// product scroll container // product scroll container
> .scroller { > .scroller {
width: 720px; width: 720px;

View File

@@ -0,0 +1,79 @@
import { memo, useCallback, useState } from "react";
import Spottable from "@enact/spotlight/Spottable";
import IcWarning from "../../../../assets/icon/ic-warning.svg";
import IcLiveShow from "../../../../assets/icon/tag/tag-liveshow.svg";
import VideoOverlayWithPhoneNumber from "../../../components/VideoOverlayWithPhoneNumber/VideoOverlayWithPhoneNumber";
import { $L } from "../../../utils/helperMethods";
import css from "./LiveVideoCard.module.less";
const SpottableComponent = Spottable("div");
const LIVE_SHOW_STRING = "Live Show";
const WARNING_STRING = "Warning";
const WARNING_MESSAGE =
"This program was previously recorded. Offers, pricing, and availability may have changed.";
export default memo(function LiveVideoCard({
endTime,
liveChannelCount,
onVideoCardBlur,
onVideoCardClick,
onVideoCardFocus,
startTime,
thumbnailSource,
title,
...rest
}) {
const [isFocused, setIsFocused] = useState(false);
const handleBlur = useCallback(() => {
setIsFocused(false);
onVideoCardBlur && onVideoCardBlur();
}, [onVideoCardBlur]);
const handleClick = useCallback(() => {
onVideoCardClick && onVideoCardClick();
}, [onVideoCardClick]);
const handleFocus = useCallback(() => {
setIsFocused(true);
onVideoCardFocus && onVideoCardFocus();
}, [onVideoCardFocus]);
return (
<SpottableComponent
className={css.container}
{...rest}
onBlur={handleBlur}
onClick={handleClick}
onFocus={handleFocus}
>
<img src={thumbnailSource} alt={title} />
<div>
<div>
<img src={IcLiveShow} alt={LIVE_SHOW_STRING} />
<div>
<h3>{title}</h3>
<time>
{startTime} ~ {endTime}
</time>
</div>
</div>
{liveChannelCount && liveChannelCount === 1 && (
<div>
<img src={IcWarning} alt={WARNING_STRING} />
<span>{$L(WARNING_MESSAGE)}</span>
</div>
)}
</div>
<VideoOverlayWithPhoneNumber
className={css.callToOrder}
show={isFocused}
/>
</SpottableComponent>
);
});

View File

@@ -0,0 +1,77 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.container {
/* normal */
position: relative;
z-index: 10;
.size(@w: 1002px, @h: 564px);
margin-right: 18px;
border: 4px solid transparent;
border-radius: 12px;
overflow: hidden;
// image(thumbnail) area
> img {
width: 100%;
}
// contents area
> div:nth-child(2) {
.position(@position: absolute, @top: 0, @right: 0, @left: 0);
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
.size(@w: 100%, @h: 100%);
background-image: linear-gradient(to top, transparent 55%, @COLOR_BLACK);
// top part of video
> div:nth-child(1) {
.flex(@justifyCenter: flex-start);
gap: 12px;
width: 100%;
padding: 18px;
color: @COLOR_WHITE;
img {
.size(@w: 108px, @h: 48px);
}
div {
h3 {
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
.elip(@clamp:1);
word-break: break-all;
}
time {
.font(@fontFamily: @baseFont, @fontSize: 24px);
}
}
}
// bottom part of video
> div:nth-child(2) {
.flex(@justifyCenter: flex-start);
gap: 12px;
padding: 6px 18px 18px;
.size(@w: 100%, @h: 54px);
background-color: rgba(0, 0, 0, 0.9);
.font(@fontFamily: @baseFont, @fontSize: 20px);
color: @COLOR_GRAY04;
img {
.size(@w: 18px, @h: 18px);
}
}
}
/* focused */
&:focus-within {
border: 4px solid @PRIMARY_COLOR_RED;
.focusDropShadow();
}
}
.callToOrder {
bottom: 64px;
left: 18px;
}

View File

@@ -0,0 +1,6 @@
{
"main": "LiveVideoCard.jsx",
"styles": [
"LiveVideoCard.module.less"
]
}