[FeaturedBrandsPanel] UpComing UI 구현
Detail Notes : 1. UpComing, UpComingList, UpComingCard, UI 구현
This commit is contained in:
@@ -15,6 +15,7 @@ import Banner from "./Banner/Banner";
|
||||
import css from "./FeaturedBrandsPanel.module.less";
|
||||
import LiveChannels from "./LiveChannels/LiveChannels";
|
||||
import QuickMenu from "./QuickMenu/QuickMenu";
|
||||
import UpComing from "./UpComing/UpComing";
|
||||
|
||||
const getSelectedBrandInfo = (brandInfo, selectedPatnrId) => {
|
||||
return brandInfo.find((brand) => brand?.patnrId === selectedPatnrId);
|
||||
@@ -34,6 +35,9 @@ export default function FeaturedBrandsPanel() {
|
||||
const brandChannelCnt = useSelector(
|
||||
(state) => state.brand.brandLiveChannelInfoData.brandChannelCnt
|
||||
);
|
||||
const brandLiveChannelUpcoming = useSelector(
|
||||
(state) => state.brand.brandLiveChannelInfoData.brandLiveChannelUpcoming
|
||||
);
|
||||
|
||||
const [selectedPatnrId, setSelectedPatnrId] = useState(String(panelInfo));
|
||||
const [selectedBrandInfo, setSelectedBrandInfo] = useState();
|
||||
@@ -73,7 +77,7 @@ export default function FeaturedBrandsPanel() {
|
||||
return (
|
||||
/* scenario page 98 */
|
||||
<TPanel className={css.container}>
|
||||
<TBody>
|
||||
<TBody className={css.tBody}>
|
||||
{brandInfo && brandInfo.length > 1 && (
|
||||
<QuickMenu
|
||||
brandInfo={brandInfo}
|
||||
@@ -96,6 +100,10 @@ export default function FeaturedBrandsPanel() {
|
||||
brandChannelCnt={brandChannelCnt}
|
||||
/>
|
||||
)}
|
||||
|
||||
{brandLiveChannelUpcoming && brandLiveChannelUpcoming.length > 0 && (
|
||||
<UpComing brandLiveChannelUpcoming={brandLiveChannelUpcoming} />
|
||||
)}
|
||||
</div>
|
||||
</TBody>
|
||||
</TPanel>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
@import "../../style/CommonStyle.module.less";
|
||||
@import "../../style/utils.module.less";
|
||||
|
||||
.container {
|
||||
background-color: @BG_COLOR_01;
|
||||
|
||||
.tBody {
|
||||
height: 1080px;
|
||||
}
|
||||
.sectionContainer {
|
||||
padding-left: 60px;
|
||||
|
||||
.sectionContainer {
|
||||
padding-left: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
|
||||
import SectionTitle from "../../../components/SectionTitle/SectionTitle";
|
||||
import { $L } from "../../../utils/helperMethods";
|
||||
import css from "./UpComing.module.less";
|
||||
import UpComingList from "./UpComingList/UpComingList";
|
||||
|
||||
const UPCOMING_STRING = "UPCOMING";
|
||||
|
||||
export default function UpComing({ brandLiveChannelUpcoming }) {
|
||||
return (
|
||||
<div className={css.container}>
|
||||
<SectionTitle title={$L(UPCOMING_STRING)} />
|
||||
<UpComingList brandLiveChannelUpcoming={brandLiveChannelUpcoming} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@import "../../../style/CommonStyle.module.less";
|
||||
@import "../../../style/utils.module.less";
|
||||
|
||||
.container {
|
||||
h2 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import React, { memo, useCallback, useState } from "react";
|
||||
|
||||
import classNames from "classnames";
|
||||
|
||||
import Spottable from "@enact/spotlight/Spottable";
|
||||
|
||||
import IcUpComingsNormal from "../../../../../../assets/icon/badge/ic-upcomings-nor@3x.png";
|
||||
import IcUpComingsSelected from "../../../../../../assets/icon/badge/ic-upcomings-sel@3x.png";
|
||||
import { $L } from "../../../../../utils/helperMethods";
|
||||
import css from "./UpComingCard.module.less";
|
||||
|
||||
const STRING_CONF = {
|
||||
REMINDER_SETTED: $L("Reminder Setted"),
|
||||
REMOVE_REMINDER: $L("Remove Reminder"),
|
||||
SET_REMINDER: $L("Set Reminder"),
|
||||
WITH_HOST: $L("With Host"),
|
||||
};
|
||||
|
||||
const SpottableComponent = Spottable("div");
|
||||
|
||||
export default memo(function UpComingCard({
|
||||
hostName,
|
||||
onUpComingCardBlur,
|
||||
onUpComingCardClick,
|
||||
onUpComingCardFocus,
|
||||
showName,
|
||||
startTime,
|
||||
...rest
|
||||
}) {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setIsFocused(false);
|
||||
onUpComingCardBlur && onUpComingCardBlur();
|
||||
}, [onUpComingCardBlur]);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (isSelected) {
|
||||
setIsSelected(false);
|
||||
// @@pyh, Todo toast popup and action
|
||||
console.log(
|
||||
"@@\nReminder Notification is turned off.\nSelect Yes to turn on reminders."
|
||||
);
|
||||
} else {
|
||||
setIsSelected(true);
|
||||
}
|
||||
|
||||
onUpComingCardClick && onUpComingCardClick();
|
||||
}, [isSelected, onUpComingCardClick]);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
setIsFocused(true);
|
||||
onUpComingCardFocus && onUpComingCardFocus();
|
||||
}, [onUpComingCardFocus]);
|
||||
|
||||
return (
|
||||
<SpottableComponent
|
||||
className={classNames(css.item, isSelected && css.selected)}
|
||||
onBlur={handleBlur}
|
||||
onClick={handleClick}
|
||||
onFocus={handleFocus}
|
||||
{...rest}
|
||||
>
|
||||
{!isFocused && (
|
||||
<div>
|
||||
<img
|
||||
src={isSelected ? IcUpComingsSelected : IcUpComingsNormal}
|
||||
alt=""
|
||||
/>
|
||||
{isSelected && <span>{STRING_CONF.REMINDER_SETTED}</span>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<time>
|
||||
{
|
||||
// @@pyh, Todo 시간 변환
|
||||
startTime
|
||||
}
|
||||
</time>
|
||||
<h3>{showName}</h3>
|
||||
<p>{STRING_CONF.WITH_HOST + " " + hostName}</p>
|
||||
|
||||
{isFocused && (
|
||||
<button type="button">
|
||||
{isSelected ? STRING_CONF.REMOVE_REMINDER : STRING_CONF.SET_REMINDER}
|
||||
</button>
|
||||
)}
|
||||
</SpottableComponent>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
@import "../../../../../style/CommonStyle.module.less";
|
||||
@import "../../../../../style/utils.module.less";
|
||||
|
||||
.item {
|
||||
/* normal */
|
||||
position: relative;
|
||||
.size(@w: 480px, @h: 344px);
|
||||
padding: 30px;
|
||||
border: solid 1px @COLOR_GRAY02;
|
||||
border-radius: 12px;
|
||||
background-color: @COLOR_WHITE;
|
||||
|
||||
> div:nth-child(1) {
|
||||
.flex(@justifyCenter: flex-start);
|
||||
margin-bottom: 30px;
|
||||
|
||||
img {
|
||||
.size(@w: 72px, @h: 72px);
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
span {
|
||||
.font(@fontFamily: @baseFontBold, @fontSize: 36px);
|
||||
color: @COLOR_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
time {
|
||||
display: inline-block;
|
||||
min-height: 42px;
|
||||
margin-bottom: 20px;
|
||||
.font(@fontFamily: @baseFontBold, @fontSize: 36px);
|
||||
color: @PRIMARY_COLOR_RED;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 15px;
|
||||
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
|
||||
color: @COLOR_GRAY06;
|
||||
.elip(@clamp:2);
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 18px;
|
||||
.font(@fontFamily: @baseFont, @fontSize: 24px);
|
||||
color: @COLOR_GRAY04;
|
||||
.elip(@clamp:1);
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
.position(@position: absolute, @right: 30px, @bottom: 30px, @left: 30px);
|
||||
.flex();
|
||||
.size(@w: 420px, @h: 84px);
|
||||
border-radius: 10px;
|
||||
background-color: @PRIMARY_COLOR_RED;
|
||||
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
|
||||
color: @COLOR_WHITE;
|
||||
}
|
||||
|
||||
/* normal focused */
|
||||
&:focus-within {
|
||||
&::after {
|
||||
.focused(@boxShadow:50px, @borderRadius:12px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* selected */
|
||||
.selected {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
time {
|
||||
color: @SELECTED_COLOR_RED;
|
||||
}
|
||||
|
||||
p {
|
||||
color: @COLOR_GRAY05;
|
||||
}
|
||||
|
||||
/* selected focused */
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"main": "UpComingCard.jsx",
|
||||
"styles": [
|
||||
"UpComingCard.module.less"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import { VirtualGridList } from "@enact/sandstone/VirtualList";
|
||||
|
||||
import { scaleH, scaleW } from "../../../../utils/helperMethods";
|
||||
import UpComingCard from "./UpComingCard/UpComingCard";
|
||||
import css from "./UpComingList.module.less";
|
||||
|
||||
const LIST_ITEM_CONF = {
|
||||
ITEM_WIDTH: 480,
|
||||
ITEM_HEIGHT: 344,
|
||||
SAPCING: 18,
|
||||
};
|
||||
|
||||
export default function UpComingList({ brandLiveChannelUpcoming }) {
|
||||
const renderItem = useCallback(
|
||||
({ index, ...rest }) => {
|
||||
const {
|
||||
hstNm: hostName,
|
||||
showNm: showName,
|
||||
strtDt: startTime,
|
||||
} = brandLiveChannelUpcoming[index];
|
||||
return (
|
||||
<UpComingCard
|
||||
{...rest}
|
||||
hostName={hostName}
|
||||
showName={showName}
|
||||
startTime={startTime}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[brandLiveChannelUpcoming]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={css.container}>
|
||||
{brandLiveChannelUpcoming && (
|
||||
<VirtualGridList
|
||||
className={css.virtualGridList}
|
||||
dataSize={brandLiveChannelUpcoming.length}
|
||||
direction="horizontal"
|
||||
itemRenderer={renderItem}
|
||||
itemSize={{
|
||||
minWidth: scaleW(LIST_ITEM_CONF.ITEM_WIDTH),
|
||||
minHeight: scaleH(LIST_ITEM_CONF.ITEM_HEIGHT),
|
||||
}}
|
||||
noScrollByWheel
|
||||
scrollMode="translate"
|
||||
spacing={scaleW(LIST_ITEM_CONF.SAPCING)}
|
||||
verticalScrollbar="hidden"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@import "../../../../style/CommonStyle.module.less";
|
||||
@import "../../../../style/utils.module.less";
|
||||
|
||||
.container {
|
||||
.size(@w: 100%, @h: 344px);
|
||||
|
||||
.virtualGridList {
|
||||
overflow: unset;
|
||||
|
||||
> div {
|
||||
overflow: unset !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"main": "UpComingList.jsx",
|
||||
"styles": [
|
||||
"UpComingList.module.less"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"main": "UpComming.jsx",
|
||||
"styles": [
|
||||
"UpComming.module.less"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user