[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:
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
{brandShopByShowContsList &&
|
||||
brandShopByShowContsList.map(({ contsId, contsNm }, index) => (
|
||||
<li key={"shop-by-show-conts-" + index}>
|
||||
<TButton
|
||||
className={!selectedClctId && css.selected}
|
||||
onClick={handleClick(null, null)}
|
||||
className={brandShopByShowContsInfo?.contsId === contsId && css.selected}
|
||||
onClick={handleClick(contsId)}
|
||||
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}>
|
||||
<TButton
|
||||
className={selectedClctId && selectedClctId === clctId && css.selected}
|
||||
onClick={handleClick(clctId, clctNm)}
|
||||
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>
|
||||
))}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user