Compare commits
6 Commits
9439630bad
...
cdf0d3de04
| Author | SHA1 | Date | |
|---|---|---|---|
| cdf0d3de04 | |||
| 9ff6064bc9 | |||
| cfee554bf6 | |||
| a5fbb21d43 | |||
| 57cc6dbf20 | |||
| 74d2b827b0 |
2
com.twin.app.shoptime/.gitignore
vendored
2
com.twin.app.shoptime/.gitignore
vendored
@@ -22,3 +22,5 @@ nul
|
|||||||
OPTIMAL.md
|
OPTIMAL.md
|
||||||
.docs
|
.docs
|
||||||
|
|
||||||
|
GEMINI.md
|
||||||
|
|
||||||
|
|||||||
BIN
com.twin.app.shoptime/assets/images/featuredBrands/image-bg.png
Normal file
BIN
com.twin.app.shoptime/assets/images/featuredBrands/image-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 346 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
@@ -1,6 +1,7 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
|
|
||||||
import IcPartnersDefault from "../../../../assets/images/ic-tab-partners-default@3x.png";
|
import IcPartnersDefault from "../../../../assets/images/ic-tab-partners-default@3x.png";
|
||||||
|
import NBCULogoImage from "../../../../assets/images/featuredBrands/image-nbcu.png";
|
||||||
import CustomImage from "../../../components/CustomImage/CustomImage";
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
import css from "./Banner.module.less";
|
import css from "./Banner.module.less";
|
||||||
|
|
||||||
@@ -15,16 +16,20 @@ export default memo(function Banner({
|
|||||||
const { patncLogoPath, patncNm } = selectedBrandInfo;
|
const { patncLogoPath, patncNm } = selectedBrandInfo;
|
||||||
const { topImgAlt, topImgPath } = brandTopImgInfo;
|
const { topImgAlt, topImgPath } = brandTopImgInfo;
|
||||||
|
|
||||||
|
// NBCU 로고 이미지 처리
|
||||||
|
const logoSrc = panelPatnrId === 'NBCU' ? NBCULogoImage : patncLogoPath;
|
||||||
|
const logoName = panelPatnrId === 'NBCU' ? 'Peacock' : patncNm;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={css.container}>
|
<div className={css.container}>
|
||||||
<figure>
|
<figure>
|
||||||
<CustomImage
|
<CustomImage
|
||||||
src={patncLogoPath}
|
src={logoSrc}
|
||||||
alt={patncNm}
|
alt={logoName}
|
||||||
fallbackSrc={IcPartnersDefault}
|
fallbackSrc={IcPartnersDefault}
|
||||||
ariaLabel={patncNm}
|
ariaLabel={logoName}
|
||||||
/>
|
/>
|
||||||
<figcaption>{patncNm}</figcaption>
|
<figcaption>{logoName}</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<CustomImage src={topImgPath} alt={topImgAlt} ariaLabel={topImgAlt} />
|
<CustomImage src={topImgPath} alt={topImgAlt} ariaLabel={topImgAlt} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import css from "./FeaturedBrandsPanel.module.less";
|
|||||||
import FeaturedCategory from "./FeaturedCategory/FeaturedCategory";
|
import FeaturedCategory from "./FeaturedCategory/FeaturedCategory";
|
||||||
import FeaturedCreators from "./FeaturedCreators/FeaturedCreators";
|
import FeaturedCreators from "./FeaturedCreators/FeaturedCreators";
|
||||||
import LiveChannels from "./LiveChannels/LiveChannels";
|
import LiveChannels from "./LiveChannels/LiveChannels";
|
||||||
|
import NBCUContent from "./NBCUContent/NBCUContent";
|
||||||
import QuickMenu from "./QuickMenu/QuickMenu";
|
import QuickMenu from "./QuickMenu/QuickMenu";
|
||||||
import RecommendedShows from "./RecommendedShows/RecommendedShows";
|
import RecommendedShows from "./RecommendedShows/RecommendedShows";
|
||||||
import Series from "./Series/Series";
|
import Series from "./Series/Series";
|
||||||
@@ -68,6 +69,7 @@ import TodaysDeals from "./TodaysDeals/TodaysDeals";
|
|||||||
import UpComing from "./UpComing/UpComing";
|
import UpComing from "./UpComing/UpComing";
|
||||||
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
|
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
|
||||||
import { sortedIndexOf } from "lodash";
|
import { sortedIndexOf } from "lodash";
|
||||||
|
import NBCUBgImage from "../../../assets/images/featuredBrands/image-bg.png";
|
||||||
|
|
||||||
const STRING_CONF = {
|
const STRING_CONF = {
|
||||||
CANCEL: "CANCEL",
|
CANCEL: "CANCEL",
|
||||||
@@ -81,6 +83,7 @@ const STRING_CONF = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TEMPLATE_CODE_CONF = {
|
const TEMPLATE_CODE_CONF = {
|
||||||
|
NBCU: "NBU00100",
|
||||||
LIVE_CHANNELS: "BRD00101",
|
LIVE_CHANNELS: "BRD00101",
|
||||||
UP_COMING: "BRD00102",
|
UP_COMING: "BRD00102",
|
||||||
TODAYS_DEALS: "BRD00103",
|
TODAYS_DEALS: "BRD00103",
|
||||||
@@ -304,8 +307,42 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const sortedBrandLayoutInfo = useMemo(
|
const sortedBrandLayoutInfo = useMemo(
|
||||||
() => brandLayoutInfo?.sort((a, b) => a.expsOrd - b.expsOrd) ?? [],
|
() => {
|
||||||
[brandLayoutInfo]
|
if (!panelInfo?.patnrId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// NBCU 특별 처리
|
||||||
|
if (panelInfo?.patnrId === 'NBCU') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
shptmBrndOptTpCd: TEMPLATE_CODE_CONF.NBCU,
|
||||||
|
shptmBrndOptTpNm: 'NBCU',
|
||||||
|
expsOrd: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return brandLayoutInfo?.sort((a, b) => a.expsOrd - b.expsOrd) ?? [];
|
||||||
|
},
|
||||||
|
[brandLayoutInfo, panelInfo?.patnrId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const processedBrandTopImgInfo = useMemo(
|
||||||
|
() => {
|
||||||
|
// NBCU 특별 처리
|
||||||
|
if (panelInfo?.patnrId === 'NBCU') {
|
||||||
|
return {
|
||||||
|
topImgPath: NBCUBgImage,
|
||||||
|
topImgAlt: 'NBCU Background Image',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 다른 브랜드: brandTopImgInfo가 유효한 객체여야 함
|
||||||
|
if (brandTopImgInfo && brandTopImgInfo.topImgPath) {
|
||||||
|
return brandTopImgInfo;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
[brandTopImgInfo, panelInfo?.patnrId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const doSendLogGNB = useCallback(
|
const doSendLogGNB = useCallback(
|
||||||
@@ -415,6 +452,21 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
<>
|
<>
|
||||||
{sortedBrandLayoutInfo.map((el, idx) => {
|
{sortedBrandLayoutInfo.map((el, idx) => {
|
||||||
switch (el.shptmBrndOptTpCd) {
|
switch (el.shptmBrndOptTpCd) {
|
||||||
|
case TEMPLATE_CODE_CONF.NBCU: {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={el.shptmBrndOptTpCd}>
|
||||||
|
<NBCUContent
|
||||||
|
handleItemFocus={handleItemFocus}
|
||||||
|
spotlightId={TEMPLATE_CODE_CONF.NBCU}
|
||||||
|
shelfOrder={el.expsOrd}
|
||||||
|
shelfTitle={el.shptmBrndOptTpNm}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
order={idx + 1}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
case TEMPLATE_CODE_CONF.LIVE_CHANNELS: {
|
case TEMPLATE_CODE_CONF.LIVE_CHANNELS: {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={el.shptmBrndOptTpCd}>
|
<React.Fragment key={el.shptmBrndOptTpCd}>
|
||||||
@@ -709,7 +761,7 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
|
|
||||||
// effect: layout information fetching due to partner id change
|
// effect: layout information fetching due to partner id change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fromDetail) {
|
if (!fromDetail && panelInfo?.patnrId) {
|
||||||
dispatch({ type: types.RESET_BRAND_LAYOUT_INFO });
|
dispatch({ type: types.RESET_BRAND_LAYOUT_INFO });
|
||||||
dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId }));
|
dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId }));
|
||||||
setIsInitialFocusOccurred(false);
|
setIsInitialFocusOccurred(false);
|
||||||
@@ -719,13 +771,20 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
|
|
||||||
// effect: set selectedPatnrId and selectedPatncNm
|
// effect: set selectedPatnrId and selectedPatncNm
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (brandInfo) {
|
if (brandInfo || panelInfo?.patnrId) {
|
||||||
const patnrId = panelInfo?.patnrId;
|
const patnrId = panelInfo?.patnrId;
|
||||||
const patncNm = brandInfo.find((b) => b?.patnrId === patnrId).patncNm;
|
|
||||||
|
|
||||||
setSelectedPatncNm(patncNm);
|
// NBCU 특별 처리
|
||||||
|
if (patnrId === 'NBCU') {
|
||||||
if (!fromDetail) setSelectedPatnrId(patnrId);
|
setSelectedPatncNm('NBCU');
|
||||||
|
if (!fromDetail) setSelectedPatnrId('NBCU');
|
||||||
|
} else if (brandInfo) {
|
||||||
|
const brandItem = brandInfo.find((b) => b?.patnrId === patnrId);
|
||||||
|
if (brandItem) {
|
||||||
|
setSelectedPatncNm(brandItem.patncNm);
|
||||||
|
if (!fromDetail) setSelectedPatnrId(patnrId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [brandInfo, panelInfo?.patnrId]);
|
}, [brandInfo, panelInfo?.patnrId]);
|
||||||
|
|
||||||
@@ -958,10 +1017,10 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{brandInfo && brandTopImgInfo && (
|
{((brandInfo && processedBrandTopImgInfo) || panelInfo?.patnrId === 'NBCU') && processedBrandTopImgInfo && (
|
||||||
<Banner
|
<Banner
|
||||||
brandInfo={brandInfo}
|
brandInfo={brandInfo}
|
||||||
brandTopImgInfo={brandTopImgInfo}
|
brandTopImgInfo={processedBrandTopImgInfo}
|
||||||
panelPatnrId={panelInfo?.patnrId}
|
panelPatnrId={panelInfo?.patnrId}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
import React, { memo, useCallback, useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import SectionTitle from "../../../components/SectionTitle/SectionTitle";
|
||||||
|
import NBCUSectionTitle from "./NBCUSectionTitle/NBCUSectionTitle";
|
||||||
|
import { $L } from "../../../utils/helperMethods";
|
||||||
|
import css from "./NBCUContent.module.less";
|
||||||
|
import NBCUList from "./NBCUList/NBCUList";
|
||||||
|
import NBCUSeries from "./NBCUSeries/NBCUSeries";
|
||||||
|
import seriesCard1 from "../../../../assets/images/featuredBrands/series-card-1.png";
|
||||||
|
import seriesCard2 from "../../../../assets/images/featuredBrands/series-card-2.png";
|
||||||
|
import seriesCard3 from "../../../../assets/images/featuredBrands/series-card-3.png";
|
||||||
|
|
||||||
|
const STRING_CONF = {
|
||||||
|
NBCU: "NBCU",
|
||||||
|
PICKED_FOR_YOU: "PICKED FOR YOU",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock data for Series
|
||||||
|
const MOCK_BRAND_SERIES_GROUP_INFO = [
|
||||||
|
{
|
||||||
|
seriesId: "series-1",
|
||||||
|
seriesNm: "Drama Collection",
|
||||||
|
seriesImgUrl: seriesCard1,
|
||||||
|
patnrId: "nbcu-partner-1",
|
||||||
|
brandSeriesProductInfo: Array.from({ length: 6 }).map((_, i) => ({
|
||||||
|
productId: `drama-${i}`,
|
||||||
|
productNm: `Drama Show ${i + 1}`,
|
||||||
|
imageUrl: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
priceInfo: "$15.00|$10.00|N|$5.00|33%|PROMO|2025-12-31",
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seriesId: "series-2",
|
||||||
|
seriesNm: "Comedy Series",
|
||||||
|
seriesImgUrl: seriesCard2,
|
||||||
|
patnrId: "nbcu-partner-1",
|
||||||
|
brandSeriesProductInfo: Array.from({ length: 6 }).map((_, i) => ({
|
||||||
|
productId: `comedy-${i}`,
|
||||||
|
productNm: `Comedy Show ${i + 1}`,
|
||||||
|
imageUrl: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
priceInfo: "$12.00|$8.00|N|$4.00|33%|PROMO|2025-12-31",
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seriesId: "series-3",
|
||||||
|
seriesNm: "Sci-Fi Originals",
|
||||||
|
seriesImgUrl: seriesCard3,
|
||||||
|
patnrId: "nbcu-partner-1",
|
||||||
|
brandSeriesProductInfo: Array.from({ length: 6 }).map((_, i) => ({
|
||||||
|
productId: `scifi-${i}`,
|
||||||
|
productNm: `Sci-Fi Show ${i + 1}`,
|
||||||
|
imageUrl: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
priceInfo: "$18.00|$12.00|N|$6.00|33%|PROMO|2025-12-31",
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const MOCK_BRAND_SERIES_INFO = [
|
||||||
|
{ seriesId: "series-1", seriesNm: "LOVE ISLAND" },
|
||||||
|
{ seriesId: "series-2", seriesNm: "TOP CHEF" },
|
||||||
|
{ seriesId: "series-3", seriesNm: "BELOW DECK" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SpottableDiv = Spottable('div');
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
const NBCUContent = ({
|
||||||
|
handleItemFocus,
|
||||||
|
spotlightId,
|
||||||
|
shelfOrder,
|
||||||
|
selectedPatnrId,
|
||||||
|
shelfTitle,
|
||||||
|
order,
|
||||||
|
}) => {
|
||||||
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
|
const [selectedSeriesId, setSelectedSeriesId] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('[NBCUContent] Rendered. order:', order);
|
||||||
|
}, [order]);
|
||||||
|
|
||||||
|
const _handleItemFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder);
|
||||||
|
|
||||||
|
const c = Spotlight.getCurrent();
|
||||||
|
if (firstChk === 0) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
cAriaLabel = "NBCU, Heading1," + cAriaLabel;
|
||||||
|
c.setAttribute("aria-label", cAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFirstChk(1);
|
||||||
|
} else if (firstChk === 1) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
const newcAriaLabel = cAriaLabel.replace("NBCU, Heading1,", "");
|
||||||
|
c.setAttribute("aria-label", newcAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [handleItemFocus, firstChk, spotlightId, shelfOrder]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={css.container}
|
||||||
|
data-shelf-order={order}
|
||||||
|
data-wheel-point
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
>
|
||||||
|
<SectionTitle
|
||||||
|
title={$L(STRING_CONF.NBCU)}
|
||||||
|
data-title="nbcu"
|
||||||
|
label="NBCU Heading 1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<NBCUList
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
shelfOrder={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Keyword Bubble Section (Dummy) */}
|
||||||
|
{/* <div className={css.keywordContainer}>
|
||||||
|
|
||||||
|
{['Action', 'Comedy', 'Drama', 'Sci-Fi', 'Thriller', 'Romance', 'Documentary'].map((keyword, index) => (
|
||||||
|
<SpottableDiv
|
||||||
|
key={index}
|
||||||
|
className={css.keywordBubble}
|
||||||
|
onClick={() => console.log(`Clicked keyword: ${keyword}`)}
|
||||||
|
>
|
||||||
|
{keyword}
|
||||||
|
</SpottableDiv>
|
||||||
|
))}
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
{/* Picked For You Section Title */}
|
||||||
|
<NBCUSectionTitle
|
||||||
|
title={$L(STRING_CONF.PICKED_FOR_YOU)}
|
||||||
|
data-title="picked-for-you"
|
||||||
|
label="Picked For You Heading"
|
||||||
|
isBlack={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Series Component with Mock Data */}
|
||||||
|
<NBCUSeries
|
||||||
|
brandSeriesGroupInfo={MOCK_BRAND_SERIES_GROUP_INFO}
|
||||||
|
brandSeriesInfo={MOCK_BRAND_SERIES_INFO}
|
||||||
|
fromGNB={false}
|
||||||
|
fromQuickMenu={false}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
order={order}
|
||||||
|
shelfOrder={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
spotlightId={`${spotlightId}-series`}
|
||||||
|
selectedPatncNm="NBCU"
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
selectedSeriesId={selectedSeriesId}
|
||||||
|
setSelectedSeriesId={setSelectedSeriesId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(NBCUContent);
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
@import "../../../style/CommonStyle.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 50px 0; // Adjust padding as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
.keywordContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 60px; // Match side padding of other contents
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keywordBubble {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 30px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&:focus, &:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
border-color: #ffffff;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import Spotlight from '@enact/spotlight';
|
||||||
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
|
|
||||||
|
import TItemCardNew from '../../../../components/TItemCard/TItemCard.new';
|
||||||
|
import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList';
|
||||||
|
import css from './NBCUList.module.less';
|
||||||
|
|
||||||
|
// 더미 데이터 생성
|
||||||
|
// priceInfo format: originalPrice|discountedPrice|rewardFlag|discountAmount|discountRate|promotionCode|promotionDate
|
||||||
|
const DUMMY_DATA = Array.from({ length: 10 }).map((_, index) => ({
|
||||||
|
id: `nbcu-item-${index}`,
|
||||||
|
title: `NBCU Content ${index + 1}`,
|
||||||
|
imgUrl: 'assets/images/img-thumb-empty-product@3x.png',
|
||||||
|
priceInfo: '$20.00|$10.00|N|$10.00|50%|PROMO|2025-12-31',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: '' }, enterTo: 'last-focused' },
|
||||||
|
'div'
|
||||||
|
);
|
||||||
|
|
||||||
|
const NBCUList = ({ handleItemFocus, spotlightId, shelfTitle, shelfOrder }) => {
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({ index, ...rest }) => {
|
||||||
|
const item = DUMMY_DATA[index];
|
||||||
|
const labelText = `${index + 1} of ${DUMMY_DATA.length}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TItemCardNew
|
||||||
|
{...rest}
|
||||||
|
key={item.id}
|
||||||
|
imageSource={item.imgUrl}
|
||||||
|
productName={item.title}
|
||||||
|
priceInfo={item.priceInfo}
|
||||||
|
spotlightId={`nbcu-spotlightId-${index}`}
|
||||||
|
shelfId={spotlightId}
|
||||||
|
shelfLocation={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
label={labelText}
|
||||||
|
onFocus={handleItemFocus}
|
||||||
|
onClick={() => {
|
||||||
|
console.log('Clicked NBCU item:', item.title);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[handleItemFocus, spotlightId, shelfOrder, shelfTitle]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={css.container} spotlightId="nbcu-list-id">
|
||||||
|
<TVirtualGridList
|
||||||
|
dataSize={DUMMY_DATA.length}
|
||||||
|
direction="horizontal"
|
||||||
|
itemHeight={438}
|
||||||
|
itemWidth={324}
|
||||||
|
spacing={18}
|
||||||
|
renderItem={renderItem}
|
||||||
|
className={css.tVirtualGridList}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(NBCUList);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
.size(@w: 100%, @h: 438px);
|
||||||
|
padding-right: 18px;
|
||||||
|
|
||||||
|
// tVirtualGridListContainer
|
||||||
|
> div:nth-child(1) {
|
||||||
|
.size(@w: 100%, @h: inherit);
|
||||||
|
|
||||||
|
&.tVirtualGridList {
|
||||||
|
padding-left: 60px;
|
||||||
|
|
||||||
|
> div:nth-child(3) {
|
||||||
|
right: -18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React, { memo } from "react";
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import css from "./NBCUSectionTitle.module.less";
|
||||||
|
|
||||||
|
export default memo(function NBCUSectionTitle({
|
||||||
|
className,
|
||||||
|
itemCount,
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
isBlack = false,
|
||||||
|
...rest
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<h2
|
||||||
|
className={classNames(css.sectionTitle, isBlack && css.blackTitle, className)}
|
||||||
|
aria-label={label ? label : title}
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-live="polite"
|
||||||
|
aria-atomic="true"
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
{itemCount && <span>({itemCount})</span>}
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
position: relative;
|
||||||
|
.flex(@justifyCenter: flex-start);
|
||||||
|
min-height: 50px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 42px;
|
||||||
|
color: #000000 !important;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: inline-block;
|
||||||
|
content: "";
|
||||||
|
.size(@w: 6px, @h: 36px);
|
||||||
|
margin-right: 12px;
|
||||||
|
background-color: #000000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blackTitle {
|
||||||
|
color: #000000;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import React, { memo, useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import NBCUSectionTitle from "../NBCUSectionTitle/NBCUSectionTitle";
|
||||||
|
import { $L } from "../../../../utils/helperMethods";
|
||||||
|
import css from "./NBCUSeries.module.less";
|
||||||
|
import SeriesContents from "../../Series/SeriesContents/SeriesContents";
|
||||||
|
import SeriesNav from "../../Series/SeriesNav/SeriesNav";
|
||||||
|
|
||||||
|
const STRING_CONF = {
|
||||||
|
SERIES: "SERIES",
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
const NBCUSeries = ({
|
||||||
|
brandSeriesGroupInfo,
|
||||||
|
brandSeriesInfo,
|
||||||
|
fromGNB,
|
||||||
|
fromQuickMenu,
|
||||||
|
handleItemFocus,
|
||||||
|
order,
|
||||||
|
shelfOrder,
|
||||||
|
shelfTitle,
|
||||||
|
spotlightId,
|
||||||
|
selectedPatncNm,
|
||||||
|
selectedPatnrId,
|
||||||
|
selectedSeriesId,
|
||||||
|
setSelectedSeriesId,
|
||||||
|
}) => {
|
||||||
|
const [filteredBrandSeriesGroupInfo, setFilteredSeriesGroupInfo] = useState();
|
||||||
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!selectedSeriesId) {
|
||||||
|
return setFilteredSeriesGroupInfo(brandSeriesGroupInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredSeriesGroupInfo(
|
||||||
|
brandSeriesGroupInfo.filter(
|
||||||
|
({ seriesId }) => seriesId === selectedSeriesId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [brandSeriesGroupInfo, selectedSeriesId]);
|
||||||
|
|
||||||
|
const _handleItemFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder);
|
||||||
|
|
||||||
|
const c = Spotlight.getCurrent();
|
||||||
|
if (firstChk === 0) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
cAriaLabel = "series, Heading1," + cAriaLabel;
|
||||||
|
c.setAttribute("aria-label", cAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFirstChk(1);
|
||||||
|
} else if (firstChk === 1) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
const newcAriaLabel = cAriaLabel.replace("series, Heading1,", "");
|
||||||
|
c.setAttribute("aria-label", newcAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [handleItemFocus, firstChk]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={css.container}
|
||||||
|
data-shelf-order={order}
|
||||||
|
data-wheel-point
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
>
|
||||||
|
{/* <NBCUSectionTitle title={$L(STRING_CONF.SERIES)} data-title="series" isBlack={true} /> */}
|
||||||
|
<SeriesNav
|
||||||
|
brandSeriesInfo={brandSeriesInfo}
|
||||||
|
fromGNB={fromGNB}
|
||||||
|
fromQuickMenu={fromQuickMenu}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
selectedPatncNm={selectedPatncNm}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
selectedSeriesId={selectedSeriesId}
|
||||||
|
setSelectedSeriesId={setSelectedSeriesId}
|
||||||
|
/>
|
||||||
|
{filteredBrandSeriesGroupInfo &&
|
||||||
|
filteredBrandSeriesGroupInfo.map(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
brandSeriesProductInfo,
|
||||||
|
patnrId,
|
||||||
|
seriesId,
|
||||||
|
seriesImgUrl,
|
||||||
|
seriesNm,
|
||||||
|
},
|
||||||
|
contentsIndex
|
||||||
|
) => (
|
||||||
|
<SeriesContents
|
||||||
|
brandSeriesProductInfo={brandSeriesProductInfo}
|
||||||
|
filteredBrandLength={filteredBrandSeriesGroupInfo.length}
|
||||||
|
contentsIndex={contentsIndex}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
isCarousel={!selectedSeriesId}
|
||||||
|
key={`${spotlightId}-${contentsIndex}`}
|
||||||
|
patnrId={patnrId}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
selectedSeriesId={selectedSeriesId}
|
||||||
|
seriesId={seriesId}
|
||||||
|
seriesImgUrl={seriesImgUrl}
|
||||||
|
seriesNm={seriesNm}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
shelfOrder={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
selectedPatncNm={selectedPatncNm}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(NBCUSeries);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 36px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-left: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDeco
|
|||||||
import TScroller from "../../../components/TScroller/TScroller";
|
import TScroller from "../../../components/TScroller/TScroller";
|
||||||
import useScrollTo from "../../../hooks/useScrollTo";
|
import useScrollTo from "../../../hooks/useScrollTo";
|
||||||
import { scaleW } from "../../../utils/helperMethods";
|
import { scaleW } from "../../../utils/helperMethods";
|
||||||
import QuickMenuItem from "../QuickMenu/QuickMenuItem/QuickMenuItem";
|
import QuickMenuItem from "./QuickMenuItem/QuickMenuItem";
|
||||||
|
import QuickMenuItemNBCU from "./QuickMenuItemNBCU/QuickMenuItemNBCU";
|
||||||
import css from "./QuickMenu.module.less";
|
import css from "./QuickMenu.module.less";
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
@@ -76,18 +77,44 @@ const QuickMenu = ({
|
|||||||
noScrollByWheel
|
noScrollByWheel
|
||||||
>
|
>
|
||||||
<ul ref={ulRef}>
|
<ul ref={ulRef}>
|
||||||
{brandInfo.map((brandInfoItem, itemIndex) => (
|
{panelPatnrId === 'NBCU' ? (
|
||||||
<QuickMenuItem
|
<>
|
||||||
brandInfoItem={brandInfoItem}
|
<QuickMenuItemNBCU
|
||||||
itemIndex={itemIndex}
|
itemIndex={0}
|
||||||
handleItemFocus={_handleItemFocus}
|
handleItemFocus={_handleItemFocus}
|
||||||
key={"brand-info" + itemIndex}
|
key="nbcu-item"
|
||||||
resetStates={resetStates}
|
resetStates={resetStates}
|
||||||
scrollLeft={scrollLeft}
|
scrollLeft={scrollLeft}
|
||||||
selectedPatnrId={selectedPatnrId}
|
selectedPatnrId={selectedPatnrId}
|
||||||
label={itemIndex * 1 + 1 + " of " + brandInfo.length}
|
label={"1 of " + (brandInfo.length + 1)}
|
||||||
/>
|
/>
|
||||||
))}
|
{brandInfo.map((brandInfoItem, itemIndex) => (
|
||||||
|
<QuickMenuItem
|
||||||
|
brandInfoItem={brandInfoItem}
|
||||||
|
itemIndex={itemIndex + 1}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
key={"brand-info" + itemIndex}
|
||||||
|
resetStates={resetStates}
|
||||||
|
scrollLeft={scrollLeft}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
label={(itemIndex + 2) + " of " + (brandInfo.length + 1)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
brandInfo.map((brandInfoItem, itemIndex) => (
|
||||||
|
<QuickMenuItem
|
||||||
|
brandInfoItem={brandInfoItem}
|
||||||
|
itemIndex={itemIndex}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
key={"brand-info" + itemIndex}
|
||||||
|
resetStates={resetStates}
|
||||||
|
scrollLeft={scrollLeft}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
label={(itemIndex + 1) + " of " + brandInfo.length}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</TScroller>
|
</TScroller>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import React, { memo, useCallback } from "react";
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import IcPartnersDefault from "../../../../../assets/images/ic-tab-partners-default@3x.png";
|
||||||
|
import { resetPanels, updatePanel } from "../../../../actions/panelActions";
|
||||||
|
import CustomImage from "../../../../components/CustomImage/CustomImage";
|
||||||
|
import useScrollReset from "../../../../hooks/useScrollReset";
|
||||||
|
import { panel_names } from "../../../../utils/Config";
|
||||||
|
import css from "./QuickMenuItemNBCU.module.less";
|
||||||
|
|
||||||
|
const SpottableComponent = Spottable("li");
|
||||||
|
|
||||||
|
const QuickMenuItemNBCU = ({
|
||||||
|
itemIndex,
|
||||||
|
handleItemFocus,
|
||||||
|
resetStates,
|
||||||
|
scrollLeft,
|
||||||
|
selectedPatnrId,
|
||||||
|
label,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const { handleScrollReset, handleStopScrolling } = useScrollReset(
|
||||||
|
scrollLeft,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
|
||||||
|
|
||||||
|
const patnrId = "NBCU";
|
||||||
|
|
||||||
|
const handleBlur = useCallback(() => {
|
||||||
|
if (itemIndex !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStopScrolling();
|
||||||
|
}, [handleStopScrolling, itemIndex]);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (patnrId === (selectedPatnrId ?? panelInfo?.patnrId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const from = "menu";
|
||||||
|
const name = panel_names.FEATURED_BRANDS_PANEL;
|
||||||
|
|
||||||
|
dispatch(resetPanels([{ name }]));
|
||||||
|
dispatch(updatePanel({ name, panelInfo: { from, patnrId } }));
|
||||||
|
resetStates();
|
||||||
|
}, [dispatch, patnrId, resetStates, selectedPatnrId]);
|
||||||
|
|
||||||
|
const handleFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) handleItemFocus();
|
||||||
|
|
||||||
|
if (itemIndex !== 0) return;
|
||||||
|
|
||||||
|
handleScrollReset();
|
||||||
|
}, [handleScrollReset, handleItemFocus, itemIndex]);
|
||||||
|
|
||||||
|
const selected =
|
||||||
|
(selectedPatnrId ?? panelInfo?.patnrId) === patnrId ? "Selected, " : "";
|
||||||
|
const ariaLabel = selected + "Channel NBCU, Tap " + label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SpottableComponent
|
||||||
|
className={classNames(
|
||||||
|
css.brand,
|
||||||
|
(selectedPatnrId ?? panelInfo?.patnrId) === patnrId && css.selected
|
||||||
|
)}
|
||||||
|
data-menu-index={itemIndex}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onClick={handleClick}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
spotlightId={"spotlightId-NBCU"}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<CustomImage
|
||||||
|
src="assets/images/featuredBrands/image-nbcu.png"
|
||||||
|
alt="NBCU"
|
||||||
|
fallbackSrc={IcPartnersDefault}
|
||||||
|
ariaLabel="NBCU"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SpottableComponent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(QuickMenuItemNBCU);
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
position: relative;
|
||||||
|
.flex();
|
||||||
|
.size(@w: 144px, @h: 144px);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
// NBCU image
|
||||||
|
> img {
|
||||||
|
.size(@w: 120px, @h: 120px);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
// NBCU image
|
||||||
|
.size(@w: 144px, @h: 144px);
|
||||||
|
> div {
|
||||||
|
&:after {
|
||||||
|
.focused(@boxShadow: 0px, @borderRadius: 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
> img {
|
||||||
|
.size(@w: 144px, @h: 144px);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
// NBCU image
|
||||||
|
&:after {
|
||||||
|
.size(@w:100%, @h:6px);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: -18px;
|
||||||
|
background: @PRIMARY_COLOR_RED;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
> div {
|
||||||
|
&:after {
|
||||||
|
.focused(@boxShadow: 0px, @borderRadius: 50%);
|
||||||
|
border-color: @PRIMARY_COLOR_RED;
|
||||||
|
}
|
||||||
|
> img {
|
||||||
|
.size(@w: 120px, @h: 120px);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user