[251127] feat: Featured Brands - NBCU Series
🕐 커밋 시간: 2025. 11. 27. 10:42:58 📊 변경 통계: • 총 파일: 7개 • 추가: +137줄 • 삭제: -1줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUSectionTitle/NBCUSectionTitle.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUSectionTitle/NBCUSectionTitle.module.less + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUSeries/NBCUSeries.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUSeries/NBCUSeries.module.less 📝 수정된 파일: ~ com.twin.app.shoptime/.gitignore ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUContent.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/NBCUContent/NBCUContent.module.less 🔧 주요 변경 내용: • 중간 규모 기능 개선 • 모듈 구조 개선
This commit is contained in:
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,69 @@
|
|||||||
import React, { memo, useCallback, useState } from "react";
|
import React, { memo, useCallback, useState, useEffect } from "react";
|
||||||
|
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from "@enact/spotlight";
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
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 NBCUSectionTitle from "./NBCUSectionTitle/NBCUSectionTitle";
|
||||||
import { $L } from "../../../utils/helperMethods";
|
import { $L } from "../../../utils/helperMethods";
|
||||||
import css from "./NBCUContent.module.less";
|
import css from "./NBCUContent.module.less";
|
||||||
import NBCUList from "./NBCUList/NBCUList";
|
import NBCUList from "./NBCUList/NBCUList";
|
||||||
|
import NBCUSeries from "./NBCUSeries/NBCUSeries";
|
||||||
|
|
||||||
const STRING_CONF = {
|
const STRING_CONF = {
|
||||||
NBCU: "NBCU",
|
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: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
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: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
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: "assets/images/img-thumb-empty-product@3x.png",
|
||||||
|
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(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
"div"
|
"div"
|
||||||
@@ -26,6 +78,11 @@ const NBCUContent = ({
|
|||||||
order,
|
order,
|
||||||
}) => {
|
}) => {
|
||||||
const [firstChk, setFirstChk] = useState(0);
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
|
const [selectedSeriesId, setSelectedSeriesId] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('[NBCUContent] Rendered. order:', order);
|
||||||
|
}, [order]);
|
||||||
|
|
||||||
const _handleItemFocus = useCallback(() => {
|
const _handleItemFocus = useCallback(() => {
|
||||||
if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder);
|
if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder);
|
||||||
@@ -65,6 +122,9 @@ const NBCUContent = ({
|
|||||||
data-title="nbcu"
|
data-title="nbcu"
|
||||||
label="NBCU Heading 1"
|
label="NBCU Heading 1"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<NBCUList
|
<NBCUList
|
||||||
handleItemFocus={_handleItemFocus}
|
handleItemFocus={_handleItemFocus}
|
||||||
spotlightId={spotlightId}
|
spotlightId={spotlightId}
|
||||||
@@ -72,6 +132,45 @@ const NBCUContent = ({
|
|||||||
shelfTitle={shelfTitle}
|
shelfTitle={shelfTitle}
|
||||||
selectedPatnrId={selectedPatnrId}
|
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>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1,36 @@
|
|||||||
@import "../../../style/CommonStyle.module.less";
|
@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,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user