[251209] feat: NBCU-ShopByShow-2

🕐 커밋 시간: 2025. 12. 09. 15:48:17

📊 변경 통계:
  • 총 파일: 11개
  • 추가: +25줄
  • 삭제: -114줄

📁 추가된 파일:
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.figma.jsx
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowImageCard/ShopByShowImageCard.jsx
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowImageCard/ShopByShowImageCard.module.less
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowProductList/ShopByShowProductList.jsx
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowProductList/ShopByShowProductList.module.less
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowSection.jsx
  + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowSection/ShopByShowSection.module.less

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.module.less
  ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.jsx
  ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.module.less
  ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.jsx

🔧 주요 변경 내용:
  • 소규모 기능 개선
  • 코드 정리 및 최적화
  • 모듈 구조 개선

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-12-09 15:48:19 +09:00
parent c7f6bf00b9
commit 2a1cda560c
11 changed files with 533 additions and 114 deletions

View File

@@ -0,0 +1,79 @@
<div style={{width: '100%', height: '100%', paddingTop: 63, paddingLeft: 60, flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{width: 1800, height: 42, justifyContent: 'flex-start', alignItems: 'center', gap: 12, display: 'inline-flex'}}>
<div style={{width: 6, height: 36, background: '#C70850'}} />
<div style={{textAlign: 'center', color: 'black', fontSize: 42, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 42, wordWrap: 'break-word'}}>Chef Gadget's</div>
</div>
<div style={{alignSelf: 'stretch', paddingTop: 20, paddingBottom: 20, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 19, display: 'inline-flex'}}>
<div style={{width: 665, alignSelf: 'stretch', padding: 18, background: 'white', overflow: 'hidden', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'flex'}}>
<img style={{flex: '1 1 0', alignSelf: 'stretch', padding: 18, background: 'linear-gradient(180deg, #EC79B8 0%, #CD4F93 100%)', border: '1px rgba(218, 218, 218, 0.54) solid'}} src="https://placehold.co/629x402" alt="Chef Gadget's featured product" />
</div>
<div style={{width: 323, padding: 18, background: 'white', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<div style={{alignSelf: 'stretch', height: 287, position: 'relative'}}>
<img style={{width: 287, height: 287, left: 0, top: 0, position: 'absolute'}} src="https://placehold.co/287x287" alt="Bravo's Top Chef product" />
<div style={{width: 71, height: 72, left: 216, top: 215, position: 'absolute', background: '#EFEEF0'}} />
</div>
<div style={{alignSelf: 'stretch', height: 82, paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Bravo's Top Chef</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
<div style={{width: 323, padding: 18, background: 'white', boxShadow: '0px 0px 30px rgba(0, 0, 0, 0.45)', borderRadius: 12, outline: '2px #C70850 solid', outlineOffset: '-2px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<img style={{alignSelf: 'stretch', height: 287}} src="https://placehold.co/287x287" alt="Top Chef Knife Tote Bag" />
<div style={{alignSelf: 'stretch', height: 82, paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Top Chef Knife Tote Bag</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
<div style={{width: 323, padding: 18, background: 'white', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<div style={{alignSelf: 'stretch', height: 287, position: 'relative'}}>
<img style={{width: 287, height: 287, left: 0, top: 0, position: 'absolute'}} src="https://placehold.co/287x287" alt="Salt, Maldon Traditional product on sale 17%" />
<div style={{width: 60, height: 60, left: 219, top: 219, position: 'absolute', background: '#C70850', borderRadius: 1000, justifyContent: 'center', alignItems: 'center', display: 'inline-flex'}}>
<div style={{justifyContent: 'center', display: 'flex', flexDirection: 'column', color: 'white', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 26, wordWrap: 'break-word'}}>17%</div>
</div>
</div>
<div style={{alignSelf: 'stretch', height: 82, paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Salt, Maldon Traditional</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
<div style={{width: 323, padding: 18, background: 'white', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<img style={{alignSelf: 'stretch', height: 287}} src="https://placehold.co/287x287" alt="Fish Grill Pan" />
<div style={{alignSelf: 'stretch', height: 82, paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Fish Grill Pan</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
<div style={{width: 323, padding: 18, background: 'white', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<img style={{alignSelf: 'stretch', height: 287}} src="https://placehold.co/287x287" alt="Product" />
<div style={{alignSelf: 'stretch', paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Productl Nameytg Product Name Producthlyg()...</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
<div style={{width: 323, padding: 18, background: 'white', borderRadius: 12, outline: '1px #DADADA solid', outlineOffset: '-1px', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', display: 'inline-flex'}}>
<img style={{alignSelf: 'stretch', height: 287}} src="https://placehold.co/287x287" alt="Product" />
<div style={{alignSelf: 'stretch', paddingTop: 8, paddingBottom: 10, justifyContent: 'flex-start', alignItems: 'flex-start', gap: 10, display: 'inline-flex'}}>
<div style={{flex: '1 1 0', color: 'black', fontSize: 24, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 32, wordWrap: 'break-word'}}>Productl Nameytg Product Name Producthlyg()...</div>
</div>
<div style={{paddingBottom: 3, justifyContent: 'center', alignItems: 'center', gap: 4, display: 'inline-flex'}}>
<div style={{color: '#C70850', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>$32.98</div>
<div style={{color: '#808080', fontSize: 18, fontFamily: 'LG Smart UI', fontWeight: '400', textDecoration: 'line-through', lineHeight: 18, wordWrap: 'break-word'}}>$150.00</div>
</div>
</div>
</div>
</div>

View File

@@ -3,7 +3,6 @@
.container {
padding-left: 60px;
margin-bottom: 12px;
> h3 {
position: relative;
@@ -18,10 +17,6 @@
}
}
.container:last-child {
margin-bottom: 0;
}
.tVirtualGridList {
padding-right: 18px;
.size(@h: 438px);

View File

@@ -22,7 +22,7 @@ import { getBrandShopByShow } from '../../../../actions/brandActions';
import useScrollTo from '../../../../hooks/useScrollTo';
import css from './ShopByShowList.module.less';
import ShopByShowNav from './ShopByShowNav/ShopByShowNav';
import ShopByShowContents from './ShopByShowContents/ShopByShowContents';
import ShopByShowSection from './ShopByShowSection/ShopByShowSection';
const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: "last-focused" },
@@ -42,14 +42,9 @@ export default function ShopByShowList({
const dispatch = useDispatch();
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
const scrollLeftJob = useRef(new Job((func) => func(), 0));
const [selectedClctId, setSelectedClctId] = useState(null);
const brandShopByShowClctInfos = brandShopByShowContsInfo?.brandShopByShowClctInfos || [];
const filteredCollections = selectedClctId
? brandShopByShowClctInfos.filter(({ clctId }) => clctId === selectedClctId)
: brandShopByShowClctInfos;
useEffect(() => {
if (panelInfo?.section !== "shop-by-show" || !panelInfo?.x) {
return;
@@ -78,7 +73,7 @@ export default function ShopByShowList({
scrollLeft();
}, [scrollLeft, selectedPatnrId]);
const handleTabClick = useCallback((contsId) => {
const handleContsIdChange = useCallback((contsId) => {
dispatch(getBrandShopByShow({ patnrId: selectedPatnrId, contsId }));
}, [selectedPatnrId, dispatch]);
@@ -94,30 +89,16 @@ export default function ShopByShowList({
id={"shop-by-show-list-id"}
spotlightId={"shop-by-show-list-id"}
>
<div className={css.tabsContainer}>
{brandShopByShowContsList && brandShopByShowContsList.map((item) => (
<button
key={item.contsId}
className={`${css.tabButton} ${brandShopByShowContsInfo?.contsId === item.contsId ? css.active : ''}`}
onClick={() => handleTabClick(item.contsId)}
onMouseDown={(e) => e.preventDefault()}
>
{item.contsNm}
</button>
))}
</div>
<ShopByShowNav
brandShopByShowClctInfos={brandShopByShowClctInfos}
brandShopByShowContsList={brandShopByShowContsList}
brandShopByShowContsInfo={brandShopByShowContsInfo}
handleItemFocus={_handleItemFocus}
selectedClctId={selectedClctId}
setSelectedClctId={setSelectedClctId}
onContsIdChange={handleContsIdChange}
/>
{filteredCollections.map((collection, collIdx) => (
<ShopByShowContents
{brandShopByShowClctInfos.map((collection, collIdx) => (
<ShopByShowSection
key={`${spotlightId}-${collIdx}`}
clctId={collection.clctId}
clctNm={collection.clctNm}
brandProductInfos={collection.brandProductInfos}
contentsIndex={collIdx}
@@ -126,7 +107,6 @@ export default function ShopByShowList({
spotlightId={spotlightId}
shelfOrder={shelfOrder}
shelfTitle={shelfTitle}
getScrollTo={getScrollTo}
/>
))}
</Container>

View File

@@ -6,54 +6,8 @@
flex-direction: column;
position: relative;
width: 100%;
}
.tabsContainer {
display: flex;
gap: 12px;
padding: 0 60px 24px 60px;
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 12px;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 2px;
}
}
.tabButton {
flex-shrink: 0;
padding: 8px 16px;
background-color: transparent;
border: 1px solid #ccc;
border-radius: 4px;
color: #666;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
&:hover {
border-color: #000;
color: #000;
}
&.active {
background-color: #000;
border-color: #000;
color: #fff;
}
&:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
> nav {
margin-bottom: 30px;
}
}

View File

@@ -12,23 +12,20 @@ const Container = SpotlightContainerDecorator(
"nav"
);
const STRING_CONF = {
ALL: "ALL",
};
export default memo(function ShopByShowNav({
brandShopByShowClctInfos,
brandShopByShowContsList,
brandShopByShowContsInfo,
handleItemFocus,
selectedClctId,
setSelectedClctId,
onContsIdChange,
}) {
const { getScrollTo, scrollLeft } = useScrollTo();
const handleClick = useCallback(
(clctId, clctNm) => () => {
setSelectedClctId(clctId);
(contsId) => () => {
onContsIdChange(contsId);
},
[setSelectedClctId]
[onContsIdChange]
);
const handleFocus = useCallback(() => {
@@ -37,41 +34,26 @@ export default memo(function ShopByShowNav({
}
}, [handleItemFocus]);
const selectedText = !selectedClctId ? "Selected " : "";
const allLabelText = selectedText + "ALL 1 of " + (brandShopByShowClctInfos?.length + 1 || 1);
return (
<Container className={css.nav} id="shop-by-show-nav-id" spotlightId="shop-by-show-nav-id">
<TScroller cbScrollTo={getScrollTo} direction="horizontal" noScrollByWheel>
<ul>
<li>
<TButton
className={!selectedClctId && css.selected}
onClick={handleClick(null, null)}
onFocus={handleFocus}
selected={!selectedClctId}
type={TYPES.oneDepthCategory}
ariaLabel={allLabelText}
>
ALL
</TButton>
</li>
{brandShopByShowClctInfos &&
brandShopByShowClctInfos.map(({ clctId, clctNm }, index) => (
<li key={"shop-by-show-clct-" + index}>
{brandShopByShowContsList &&
brandShopByShowContsList.map(({ contsId, contsNm }, index) => (
<li key={"shop-by-show-conts-" + index}>
<TButton
className={selectedClctId && selectedClctId === clctId && css.selected}
onClick={handleClick(clctId, clctNm)}
className={brandShopByShowContsInfo?.contsId === contsId && css.selected}
onClick={handleClick(contsId)}
onFocus={handleFocus}
selected={selectedClctId && selectedClctId === clctId}
selected={brandShopByShowContsInfo?.contsId === contsId}
type={TYPES.oneDepthCategory}
ariaLabel={
selectedClctId && selectedClctId === clctId
? "Selected " + clctNm + " " + (index * 1 + 2) + " of " + (brandShopByShowClctInfos.length + 1)
: "" + clctNm + " " + (index * 1 + 2) + " of " + (brandShopByShowClctInfos.length + 1)
brandShopByShowContsInfo?.contsId === contsId
? "Selected " + contsNm + " " + (index * 1 + 1) + " of " + brandShopByShowContsList.length
: "" + contsNm + " " + (index * 1 + 1) + " of " + brandShopByShowContsList.length
}
>
{clctNm}
{contsNm}
</TButton>
</li>
))}

View File

@@ -0,0 +1,22 @@
import React, { memo } from "react";
import Spottable from "@enact/spotlight/Spottable";
import CustomImage from "../../../../../../../components/CustomImage/CustomImage";
import css from "./ShopByShowImageCard.module.less";
const SpottableComponent = Spottable("figure");
export default memo(function ShopByShowImageCard({
imageAlt,
imageSource,
ariaLabel,
...rest
}) {
delete rest.clctNm;
return (
<SpottableComponent className={css.card} aria-label={ariaLabel} {...rest}>
<CustomImage src={imageSource} alt={imageAlt} />
</SpottableComponent>
);
});

View File

@@ -0,0 +1,22 @@
@import "../../../../../../../style/CommonStyle.module.less";
@import "../../../../../../../style/utils.module.less";
.card {
position: relative;
.size(@w: 665px, @h:438px);
padding: 18px;
background-color: @COLOR_WHITE;
border: solid 1px @COLOR_GRAY02;
border-radius: 12px;
img {
.size(@w: 629px, @h:402px);
object-fit: cover;
}
&:focus {
&::after {
.focused(@boxShadow: 22px, @borderRadius: 12px);
}
}
}

View File

@@ -0,0 +1,165 @@
import React, { useCallback, useRef, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import { pushPanel, updatePanel } from "../../../../../../../actions/panelActions";
import TItemCardNew, { removeDotAndColon } from "../../../../../../../components/TItemCard/TItemCard.new";
import TVirtualGridList from "../../../../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../../../../hooks/useScrollTo";
import {
LOG_CONTEXT_NAME,
LOG_MESSAGE_ID,
panel_names,
} from "../../../../../../../utils/Config";
import { getTranslate3dValueByDirection } from "../../../../../../../utils/helperMethods";
import css from "./ShopByShowProductList.module.less";
export default function ShopByShowProductList({
brandProductInfos,
contentsIndex,
handleFocus,
selectedPatnrId,
spotlightId,
shelfOrder,
shelfTitle,
clctNm,
}) {
const { getScrollTo, scrollLeft } = useScrollTo();
const dispatch = useDispatch();
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
const scrollLeftJob = useRef(new Job((func) => func(), 0));
useEffect(() => {
if (
panelInfo?.section !== "shop-by-show" ||
!panelInfo?.x ||
panelInfo?.exprOrd !== contentsIndex + 1
) {
return;
}
const scrollLeftJobValue = scrollLeftJob.current;
const { x } = panelInfo;
scrollLeftJobValue.start(() => scrollLeft({ x }));
return () => scrollLeftJobValue.stop();
}, [panelInfo, scrollLeft, contentsIndex]);
useEffect(() => {
scrollLeft();
}, [scrollLeft, selectedPatnrId]);
const handleClick = useCallback(
(patnrId, prdtId) => (e) => {
const tItemCard = e.currentTarget;
const lastFocusedTarget = Spotlight.getCurrent();
const lastFocusedTargetId = lastFocusedTarget?.getAttribute("data-spotlight-id");
const exprOrd = parseInt(
lastFocusedTarget?.getAttribute("data-exposure-order")
);
const xContainer = tItemCard?.parentNode?.parentNode;
if (exprOrd && lastFocusedTargetId && xContainer) {
const section = "shop-by-show";
const x = getTranslate3dValueByDirection(xContainer);
dispatch(
updatePanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: {
exprOrd,
lastFocusedTargetId,
patnrId,
section,
x,
},
})
);
}
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},
[dispatch]
);
const renderItem = useCallback(
({ index, ...rest }) => {
if (!brandProductInfos || !brandProductInfos[index]) {
return null;
}
const product = brandProductInfos[index];
const {
prdtImgUrl,
prdtOfferId,
patnrId = "21",
prdtNm,
prdtId,
prdtPrice,
patncNm,
brndNm,
lgCatNm,
euEnrgLblInfos,
} = product;
return (
<TItemCardNew
catNm={lgCatNm}
contextName={LOG_CONTEXT_NAME.FEATURED_BRANDS}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
patnerName={patncNm || "Peacock | Shop The Moment"}
brandName={brndNm}
shelfId={spotlightId}
shelfLocation={shelfOrder}
shelfTitle={shelfTitle}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
onClick={handleClick(patnrId, prdtId)}
onFocus={handleFocus}
offerInfo={prdtOfferId}
priceInfo={prdtPrice}
productId={prdtId}
productName={prdtNm}
spotlightId={"shop-by-show-product-list-" + removeDotAndColon(prdtId)}
data-exposure-order={contentsIndex + 1}
label={index + 1 + " of " + brandProductInfos.length}
lastLabel=" go to detail, button"
euEnrgLblInfos={euEnrgLblInfos}
{...rest}
/>
);
},
[brandProductInfos, contentsIndex, handleClick, handleFocus]
);
return (
<div className={css.container}>
{brandProductInfos && (
<TVirtualGridList
cbScrollTo={getScrollTo}
className={css.tVirtualGridList}
dataSize={brandProductInfos.length}
direction="horizontal"
itemHeight={438}
itemWidth={323}
spacing={19}
renderItem={renderItem}
/>
)}
</div>
);
}

View File

@@ -0,0 +1,30 @@
@import "../../../../../../../style/utils.module.less";
.container {
.flex();
overflow: hidden;
.size(@w: calc(100% - 665px), @h: 482px);
padding: 0 18px;
// tVirtualGridListContainer
> div:nth-child(1) {
.size(@w: 100%, @h: inherit);
> div:nth-child(1) {
padding: 22px 0;
}
> div:nth-child(3) {
right: -18px;
}
}
}
.tVirtualGridList {
padding-right: 18px;
.size(@h: 438px);
> div:nth-child(3) {
right: -18px;
}
}

View File

@@ -0,0 +1,152 @@
import React, { memo, useCallback } from "react";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import TItemCardNew, { removeDotAndColon } from "../../../../../components/TItemCard/TItemCard.new";
import TVirtualGridList from "../../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../../hooks/useScrollTo";
import {
LOG_CONTEXT_NAME,
LOG_MESSAGE_ID,
panel_names,
} from "../../../../../utils/Config";
import { getTranslate3dValueByDirection } from "../../../../../utils/helperMethods";
import { pushPanel, updatePanel } from "../../../../../actions/panelActions";
import { useDispatch } from "react-redux";
import css from "./ShopByShowSection.module.less";
const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: null },
"div"
);
const ShopByShowSection = memo(({
clctNm,
brandProductInfos,
contentsIndex,
handleItemFocus,
selectedPatnrId,
spotlightId,
shelfOrder,
shelfTitle,
}) => {
const { getScrollTo } = useScrollTo();
const dispatch = useDispatch();
const handleClick = useCallback(
(patnrId, prdtId) => (e) => {
const tItemCard = e.currentTarget;
const lastFocusedTarget = Spotlight.getCurrent();
const lastFocusedTargetId = lastFocusedTarget?.getAttribute("data-spotlight-id");
const xContainer = tItemCard?.parentNode?.parentNode;
if (lastFocusedTargetId && xContainer) {
const section = "shop-by-show";
const x = getTranslate3dValueByDirection(xContainer);
dispatch(
updatePanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: {
lastFocusedTargetId,
patnrId,
section,
x,
},
})
);
}
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},
[dispatch]
);
const handleFocus = useCallback(() => {
if (handleItemFocus) {
handleItemFocus();
}
}, [handleItemFocus]);
const renderItem = useCallback(
({ index, ...rest }) => {
if (!brandProductInfos || !brandProductInfos[index]) {
return null;
}
const product = brandProductInfos[index];
const {
prdtImgUrl,
prdtOfferId,
patnrId = "21",
prdtNm,
prdtId,
prdtPrice,
patncNm,
brndNm,
lgCatNm,
euEnrgLblInfos,
} = product;
return (
<TItemCardNew
catNm={lgCatNm}
contextName={LOG_CONTEXT_NAME.FEATURED_BRANDS}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
patnerName={patncNm || "Peacock | Shop The Moment"}
brandName={brndNm}
shelfId={spotlightId}
shelfLocation={shelfOrder}
shelfTitle={shelfTitle}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
onClick={handleClick(patnrId, prdtId)}
onFocus={handleFocus}
offerInfo={prdtOfferId}
priceInfo={prdtPrice}
productId={prdtId}
productName={prdtNm}
spotlightId={"shop-by-show-section-" + removeDotAndColon(prdtId)}
label={index * 1 + 1 + " of " + brandProductInfos.length}
lastLabel=" go to detail, button"
euEnrgLblInfos={euEnrgLblInfos}
{...rest}
/>
);
},
[brandProductInfos, handleClick, handleFocus]
);
if (!brandProductInfos || brandProductInfos.length === 0) {
return null;
}
return (
<Container
className={css.container}
data-wheel-point
spotlightId={`${spotlightId}-section-${contentsIndex}`}
>
<h3 className={css.sectionTitle}>{clctNm}</h3>
<TVirtualGridList
cbScrollTo={getScrollTo}
className={css.tVirtualGridList}
dataSize={brandProductInfos.length}
direction="horizontal"
itemHeight={438}
itemWidth={323}
spacing={19}
renderItem={renderItem}
/>
</Container>
);
});
export default ShopByShowSection;

View File

@@ -0,0 +1,38 @@
@import "../../../../../style/CommonStyle.module.less";
@import "../../../../../style/utils.module.less";
.container {
padding-left: 60px;
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
}
.sectionTitle {
position: relative;
.font(@fontFamily: @arialFontBold, @fontSize: 42px);
color: @COLOR_GRAY08;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 12px;
&::before {
content: "";
width: 6px;
height: 36px;
background-color: #C70850;
flex-shrink: 0;
}
}
.tVirtualGridList {
padding-right: 18px;
.size(@h: 438px);
> div:nth-child(3) {
right: -18px;
}
}