[FeaturedBrandsPanel] UpComing UI 구현

Detail Notes :

1. UpComing, UpComingList, UpComingCard, UI 구현
This commit is contained in:
younghoon100.park
2024-02-08 13:33:56 +09:00
parent a55eb51362
commit 55df85e912
11 changed files with 306 additions and 4 deletions

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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>
);
}

View File

@@ -0,0 +1,8 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.container {
h2 {
margin-bottom: 24px;
}
}

View File

@@ -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>
);
});

View File

@@ -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 */
}

View File

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

View File

@@ -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>
);
}

View File

@@ -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;
}
}
}

View File

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

View File

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