[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:
2025-11-27 10:42:59 +09:00
parent a5fbb21d43
commit cfee554bf6
7 changed files with 339 additions and 1 deletions

View File

@@ -22,3 +22,5 @@ nul
OPTIMAL.md OPTIMAL.md
.docs .docs
GEMINI.md

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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