[251112] fix: ShopNowContents YouMayLikeContents 구분선

🕐 커밋 시간: 2025. 11. 12. 10:53:05

📊 변경 통계:
  • 총 파일: 6개
  • 추가: +42줄
  • 삭제: -2줄

📁 추가된 파일:
  + com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ThemeContents.figma.jsx
  + com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ThemeContents.jsx
  + com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ThemeContents.module.less

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/style/CommonStyle.module.less
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.v2.module.less

🔧 주요 변경 내용:
  • 소규모 기능 개선
  • 모듈 구조 개선
This commit is contained in:
2025-11-12 10:53:07 +09:00
parent fe9781ff1c
commit 9c52cc0e03
6 changed files with 231 additions and 2 deletions

View File

@@ -77,6 +77,10 @@
@COLOR_DARK_RED: #4f172c;
/* For LiveShow 251112 */
@COLOR_DAR_GRAY: #2c2c2c;
@COLOR_LIGHT_GRAY: #7D848C;
.focusDropShadow() {
box-shadow: 0 0 22px rgba(0, 0, 0, 0.5);
}

View File

@@ -64,7 +64,8 @@ export default function ShopNowContents({
}));
// v2 + ShopNow < 3 + YouMayLike 데이터 존재 시 통합
if (version === 2 && shopNowInfo.length < 3 && youmaylikeInfos && youmaylikeInfos.length > 0) {
// if (version === 2 && shopNowInfo.length < 3 && youmaylikeInfos && youmaylikeInfos.length > 0) {
if (version === 2 && youmaylikeInfos && youmaylikeInfos.length > 0) {
items = items.concat(
youmaylikeInfos.map((item) => ({
...item,
@@ -164,6 +165,10 @@ export default function ShopNowContents({
{/* {isYouMayLikeStart && <div className={css.youMayLikeDivider} />} */}
<TItemCard
{...rest}
className={classNames(
css.youmaylikeItem,
isYouMayLikeStart && css.youmaylikeItemFirst
)}
key={prdtId}
imageAlt={prdtId}
imageSource={imgUrl}
@@ -241,6 +246,7 @@ export default function ShopNowContents({
return (
<TItemCard
{...rest}
className={css.shopnowItem}
soldoutFlag={soldoutFlag}
key={prdtId}
imageAlt={prdtId}

View File

@@ -52,11 +52,41 @@
}
}
// ShopNow 아이템 스타일
.shopnowItem {
// 현재는 기본 TItemCard 스타일 사용
// 필요시 여기에 추가 스타일 적용 가능
position: relative; // placeholder to avoid empty ruleset warning
}
// YouMayLike 아이템 스타일 (일반)
.youmaylikeItem {
// 현재는 기본 TItemCard 스타일 사용
// 필요시 여기에 추가 스타일 적용 가능
position: relative; // placeholder to avoid empty ruleset warning
}
// YouMayLike의 첫 번째 아이템 (ShopNow 다음에 오는 첫 아이템)
.youmaylikeItemFirst {
position: relative;
// 왼쪽 -15px 위치에 세로 구분선 추가
&::before {
content: '';
position: absolute;
left: -19px;
top: 0;
width: 1px;
height: 100%;
background: @COLOR_LIGHT_GRAY; //rgba(234, 234, 234, 0.3);
}
}
// YouMayLike 시작 지점 구분선
.youMayLikeDivider {
width: 2px !important;
height: 445px;
background: rgba(234, 234, 234, 0.3);
background: @COLOR_LIGHT_GRAY;
margin-right: 15px;
flex-shrink: 0;
}

View File

@@ -0,0 +1,167 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { useDispatch } from 'react-redux';
import Spotlight from '@enact/spotlight';
import { updatePanel } from '../../../../actions/panelActions';
import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList';
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config';
import PlayerItemCard, { TYPES } from '../../PlayerItemCard/PlayerItemCard';
import ListEmptyContents from '../TabContents/ListEmptyContents/ListEmptyContents';
import css from './ThemeContents.module.less';
import { sendLogTotalRecommend } from '../../../../actions/logActions';
export default function ThemeContents({
themeItems,
setSelectedIndex,
videoVerticalVisible,
currentVideoShowId,
tabIndex,
handleItemFocus,
tabTitle,
panelInfo,
direction = 'horizontal',
version = 2,
}) {
const dispatch = useDispatch();
const isClickBlocked = useRef(false);
const blockTimeoutRef = useRef(null);
const handleFocus = useCallback(
() => () => {
if (handleItemFocus) {
handleItemFocus(LOG_MENU.THEME_ITEMS);
}
},
[handleItemFocus]
);
const renderItem = useCallback(
({ index, ...rest }) => {
const {
prdtId,
prdtNm,
prdtImgPath,
salePrice,
originalPrice,
patnrLogoPath,
patncNm,
showId,
catNm,
energyLabels,
} = themeItems[index];
const handleItemClick = () => {
const params = {
tabTitle: tabTitle[tabIndex],
productId: prdtId,
productTitle: prdtNm,
showType: panelInfo?.shptmBanrTpNm,
category: catNm,
partner: patncNm,
contextName: LOG_CONTEXT_NAME.PRODUCT,
messageId: LOG_MESSAGE_ID.CONTENTCLICK,
};
dispatch(sendLogTotalRecommend(params));
// 중복클릭방지
if (isClickBlocked.current) {
return;
}
isClickBlocked.current = true;
// 이전 타이머가 있으면 정리
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
blockTimeoutRef.current = setTimeout(() => {
isClickBlocked.current = false;
blockTimeoutRef.current = null;
}, 600);
if (!prdtId) return;
setSelectedIndex(index);
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
prdtId,
showId,
shptmBanrTpNm: 'THEME',
isUpdatedByClick: true,
},
})
);
};
const productNameDangerouslySetInnerHTML = () => {
return prdtNm ? { __html: prdtNm } : { __html: '' };
};
return (
<PlayerItemCard
{...rest}
key={prdtId}
imageAlt={prdtId}
logo={patnrLogoPath}
imageSource={prdtImgPath}
videoVerticalVisible={videoVerticalVisible}
productName={productNameDangerouslySetInnerHTML}
patnerName={patncNm}
salePrice={salePrice}
originalPrice={originalPrice}
energyLabels={energyLabels}
onClick={handleItemClick}
onFocus={handleFocus()}
onSpotlightUp={
version === 2 && index === 0
? (e) => {
// v2에서 첫 번째 아이템일 때 위로 가면 THEME ITEM 버튼으로
e.stopPropagation();
e.preventDefault();
Spotlight.focus('below-tab-theme-button');
}
: undefined
}
type={TYPES.themeHorizontal}
spotlightId={`tabTheme-item-${index}`}
version={version}
/>
);
},
[themeItems, currentVideoShowId, isClickBlocked, dispatch, handleFocus, version, tabIndex, tabTitle, panelInfo, setSelectedIndex]
);
// cleanup useEffect
useEffect(() => {
return () => {
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
};
}, []);
return (
<>
<div className={css.container}>
{themeItems && themeItems.length > 0 ? (
<TVirtualGridList
dataSize={themeItems.length}
direction={direction}
renderItem={renderItem}
itemWidth={470}
itemHeight={180}
spacing={18}
noScrollByWheel={false}
/>
) : (
<ListEmptyContents tabIndex={tabIndex} />
)}
</div>
</>
);
}

View File

@@ -0,0 +1,22 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.container {
width: 100%;
height: 180px;
> div:nth-child(1) {
.size(@w: 100%, @h: 100%);
}
}
.themeList {
width: 100%;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: flex-start);
overflow-x: auto;
overflow-y: hidden;
> * + * {
margin-left: 18px;
}
}