[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:
@@ -77,6 +77,10 @@
|
|||||||
|
|
||||||
@COLOR_DARK_RED: #4f172c;
|
@COLOR_DARK_RED: #4f172c;
|
||||||
|
|
||||||
|
/* For LiveShow 251112 */
|
||||||
|
@COLOR_DAR_GRAY: #2c2c2c;
|
||||||
|
@COLOR_LIGHT_GRAY: #7D848C;
|
||||||
|
|
||||||
.focusDropShadow() {
|
.focusDropShadow() {
|
||||||
box-shadow: 0 0 22px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 22px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ export default function ShopNowContents({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// v2 + ShopNow < 3 + YouMayLike 데이터 존재 시 통합
|
// 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(
|
items = items.concat(
|
||||||
youmaylikeInfos.map((item) => ({
|
youmaylikeInfos.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
@@ -164,6 +165,10 @@ export default function ShopNowContents({
|
|||||||
{/* {isYouMayLikeStart && <div className={css.youMayLikeDivider} />} */}
|
{/* {isYouMayLikeStart && <div className={css.youMayLikeDivider} />} */}
|
||||||
<TItemCard
|
<TItemCard
|
||||||
{...rest}
|
{...rest}
|
||||||
|
className={classNames(
|
||||||
|
css.youmaylikeItem,
|
||||||
|
isYouMayLikeStart && css.youmaylikeItemFirst
|
||||||
|
)}
|
||||||
key={prdtId}
|
key={prdtId}
|
||||||
imageAlt={prdtId}
|
imageAlt={prdtId}
|
||||||
imageSource={imgUrl}
|
imageSource={imgUrl}
|
||||||
@@ -241,6 +246,7 @@ export default function ShopNowContents({
|
|||||||
return (
|
return (
|
||||||
<TItemCard
|
<TItemCard
|
||||||
{...rest}
|
{...rest}
|
||||||
|
className={css.shopnowItem}
|
||||||
soldoutFlag={soldoutFlag}
|
soldoutFlag={soldoutFlag}
|
||||||
key={prdtId}
|
key={prdtId}
|
||||||
imageAlt={prdtId}
|
imageAlt={prdtId}
|
||||||
|
|||||||
@@ -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 시작 지점 구분선
|
// YouMayLike 시작 지점 구분선
|
||||||
.youMayLikeDivider {
|
.youMayLikeDivider {
|
||||||
width: 2px !important;
|
width: 2px !important;
|
||||||
height: 445px;
|
height: 445px;
|
||||||
background: rgba(234, 234, 234, 0.3);
|
background: @COLOR_LIGHT_GRAY;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user