[251009] refactor CSS gap 대신 margin 사용 및 불필요 코드 정리messag...

변경사항을 반영했습니다

🕐 커밋 시간: 2025. 10. 09. 24:15:20

📊 변경 통계:
  • 총 파일: 8개
  • 추가: +174줄
  • 삭제: -69줄

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

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 중간 규모 기능 개선
  • 모듈 구조 개선
This commit is contained in:
2025-10-09 00:15:24 +09:00
parent 7f66fd9735
commit 3916b07b2f
8 changed files with 174 additions and 69 deletions

View File

@@ -9,8 +9,15 @@
border-radius: 12px;
border: 1px solid transparent;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: flex-start, @direction: column);
gap: 15px;
flex-shrink: 0;
> * {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
transition: all 0.3s ease;
.imageWrap {
@@ -53,7 +60,14 @@
.descWrap {
align-self: stretch;
.flex(@display: flex, @justifyCenter: center, @alignCenter: flex-start, @direction: column);
gap: 15px;
> * {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
.logo {
width: 60px;
@@ -82,8 +96,15 @@
.priceInfo {
align-self: stretch;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: center);
gap: 11px;
font-weight: 700;
> * {
margin-right: 11px;
&:last-child {
margin-right: 0;
}
}
font-size: 30px;
color: @PRIMARY_COLOR_RED;
font-family: @baseFont;
@@ -122,8 +143,6 @@
&:focus {
border-color: @PRIMARY_COLOR_RED;
outline: 4px @PRIMARY_COLOR_RED solid;
outline-offset: -4px;
&::after {
.focused(@boxShadow: 22px, @borderRadius: 12px);
@@ -132,9 +151,9 @@
}
// vertical type (v2에서는 사용하지 않음)
.vertical {
}
// .vertical {
// }
// videoShow type (v2에서는 사용하지 않음)
.videoShow {
}
// .videoShow {
// }

View File

@@ -180,6 +180,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
);
const [prevChannelIndex, setPrevChannelIndex] = USE_STATE('prevChannelIndex', 0);
const [sideContentsVisible, setSideContentsVisible] = USE_STATE('sideContentsVisible', true);
const [belowContentsVisible, setBelowContentsVisible] = USE_STATE('belowContentsVisible', true);
const [currentTime, setCurrentTime] = USE_STATE('currentTime', 0);
const [isInitialFocusOccurred, setIsInitialFocusOccurred] = USE_STATE(
'isInitialFocusOccurred',
@@ -202,8 +203,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
isFullMediaLogReady: false,
isDetailMediaReady: false,
});
const [isVODPaused, setIsVODPaused] = USE_STATE('isVODPaused', false);
const [belowTabMode, setBelowTabMode] = USE_STATE('belowTabMode', 'liveShow');
const [isVODPaused, setIsVODPaused] = USE_STATE('isVODPaused', false);
const [tabIndexV2, setTabIndexV2] = USE_STATE('tabIndexV2', 1); // 0: ShopNow, 1: LiveChannel, 2: ShopNowButton
const panels = USE_SELECTOR('panels', (state) => state.panels.panels);
@@ -1744,27 +1744,18 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
!panelInfo?.modal &&
isOnTop
);
}, [sideContentsVisible, playListInfo, panelInfo, isOnTop]);
const [belowContentsVisible, setBelowContentsVisible] = USE_STATE('belowContentsVisible', true);
}, [sideContentsVisible, playListInfo, panelInfo, isOnTop]);
const showBelowContents = useMemo(() => {
return (
belowContentsVisible &&
sideContentsVisible &&
playListInfo &&
panelInfo?.shptmBanrTpNm !== 'MEDIA' &&
!panelInfo?.modal &&
isOnTop
);
}, [belowContentsVisible, playListInfo, panelInfo, isOnTop]);
const shouldShowBelowTab = useMemo(() => {
// LiveShowContainer, ShopNowContainer, ShopNowButton 모두 표시 여부
return (
belowContentsVisible && !panelInfo?.modal && isOnTop && panelInfo?.shptmBanrTpNm !== 'MEDIA'
);
}, [belowContentsVisible, panelInfo?.modal, isOnTop, panelInfo?.shptmBanrTpNm]);
}, [belowContentsVisible, playListInfo, panelInfo, isOnTop]);
const qrCurrentItem = useMemo(() => {
if (shopNowInfo?.length && panelInfo?.shptmBanrTpNm === 'LIVE') {
return shopNowInfo[shopNowInfo.length - 1];
@@ -1812,14 +1803,14 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
timerId.current = setTimeout(() => {
setSideContentsVisible(false);
setBelowContentsVisible(false);
// setBelowContentsVisible(false);
}, timeout);
}, []);
useEffect(() => {
if (isOnTop && !panelInfo.modal && !videoVerticalVisible) {
setSideContentsVisible(true);
setBelowContentsVisible(true);
// setBelowContentsVisible(true);
}
}, [panelInfo.modal]);
@@ -1974,11 +1965,11 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
spotlightRestrict="self-only"
spotlightId="spotlightId-video-contaienr"
onKeyDown={onKeyDown}
onClick={() => {
if (!panelInfo?.modal && isOnTop && panelInfo?.shptmBanrTpNm !== 'MEDIA') {
setBelowContentsVisible((prev) => !prev);
}
}}
// onClick={() => {
// if (!panelInfo?.modal && isOnTop && panelInfo?.shptmBanrTpNm !== 'MEDIA') {
// setBelowContentsVisible((prev) => !prev);
// }
// }}
>
{isReadyToPlay && (
<VideoPlayer
@@ -2115,7 +2106,8 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
</>
)} */}
{shouldShowBelowTab && (
{/* {showBelowContents && ( */}
{ isOnTop &&
<TabContainerV2
panelInfo={panelInfo}
playListInfo={playListInfo}
@@ -2131,8 +2123,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
tabIndex={tabIndexV2}
onShopNowButtonClick={() => setTabIndexV2(0)}
onLiveChannelButtonClick={() => setTabIndexV2(2)}
onTabClose={(newTabIndex) => setTabIndexV2(newTabIndex)}
/>
)}
}
{/* )} */}
</Container>
{activePopup === ACTIVE_POPUP.alertPopup && (

View File

@@ -191,7 +191,7 @@ export default function ShopNowContents({
<Container className={css.container}>
{shopNowInfo && shopNowInfo.length > 0 ? (
<TVirtualGridList
style={gridStyle}
style={version === 2 ? undefined : gridStyle}
cbScrollTo={getScrollTo}
dataSize={shopNowInfo.length}
direction={direction}

View File

@@ -7,7 +7,8 @@
overflow: hidden;
> div:nth-child(1) {
.size(@w: 100%, @h: 100%);
width: 100%;
height: 420px;
}
}

View File

@@ -90,17 +90,19 @@ export default function YouMayLikeContents({
<div className={css.title}>{$L("You may also like")}</div>
<div className={css.line}></div>
<TVirtualGridList
style={gridStyle}
dataSize={youmaylikeInfos.length}
direction={direction}
renderItem={renderItem}
itemWidth={version === 2 ? 310 : 600}
itemHeight={version === 2 ? 420 : 236}
spacing={version === 2 ? 30 : 12}
className={css.itemList}
noScrollByWheel={false}
/>
<div className={version === 2 ? css.container : ''}>
<TVirtualGridList
style={version === 2 ? undefined : gridStyle}
dataSize={youmaylikeInfos.length}
direction={direction}
renderItem={renderItem}
itemWidth={version === 2 ? 310 : 600}
itemHeight={version === 2 ? 420 : 236}
spacing={version === 2 ? 30 : 12}
className={css.itemList}
noScrollByWheel={false}
/>
</div>
</>
)}
</>

View File

@@ -16,6 +16,17 @@
margin-bottom: 24px;
}
.container {
width: 100%;
height: 420px;
overflow: hidden;
> div:nth-child(1) {
width: 100%;
height: 420px;
}
}
.itemList {
width: 100%;
overflow: hidden;

View File

@@ -7,6 +7,7 @@ import Spottable from "@enact/spotlight/Spottable";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { LOG_MENU } from "../../../../utils/Config";
import { $L } from "../../../../utils/helperMethods";
import css from "./TabContainer.v2.module.less";
import LiveChannelContents from "../TabContents/LiveChannelContents";
import ShopNowContents from "../TabContents/ShopNowContents";
@@ -37,7 +38,15 @@ export default function TabContainerV2({
tabIndex = 1, // tabIndex prop으로 제어 (0: ShopNow, 1: LiveChannel, 2: ShopNowButton)
onShopNowButtonClick,
onLiveChannelButtonClick,
onTabClose, // 탭 닫기 콜백 함수
}) {
const tabList = [
$L("SHOP NOW"),
panelInfo?.shptmBanrTpNm === "LIVE"
? $L("LIVE CHANNEL")
: $L("FEATURED SHOWS"),
];
useEffect(() => {
let nowMenu;
@@ -77,6 +86,21 @@ export default function TabContainerV2({
[videoVerticalVisible]
);
const handleCloseButtonClick = useCallback(
(e) => {
e.stopPropagation();
e.preventDefault();
if (onTabClose) {
onTabClose(-1); // tabIndex를 -1로 설정
}
},
[onTabClose]
);
useEffect(()=>{
console.log('[tabIndex]',tabIndex)
},[tabIndex])
return (
<Container
className={classNames(
@@ -89,12 +113,21 @@ export default function TabContainerV2({
{tabIndex === 0 && (
<>
<div className={css.shopNowHeader}>
<div className={css.shopNowIconWrapper}>
<img src={icon_shop_now} alt="shop now icon" className={css.shopNowIcon} />
<div className={css.shopNowHeaderLeft}>
<div className={css.shopNowIconWrapper}>
<img src={icon_shop_now} alt="shop now icon" className={css.shopNowIcon} />
</div>
<div className={css.shopNowHeaderText}>SHOP NOW</div>
</div>
<div
className={css.closeButton}
onClick={handleCloseButtonClick}
>
×
</div>
<div className={css.shopNowHeaderText}>SHOP NOW</div>
</div>
<ShopNowContents
tabTitle={tabList}
shopNowInfo={shopNowInfo}
playListInfo={playListInfo && playListInfo[selectedIndex]}
videoVerticalVisible={videoVerticalVisible}
@@ -104,6 +137,15 @@ export default function TabContainerV2({
version={2}
direction="horizontal"
/>
{shopNowInfo && shopNowInfo.length < 3 && (
<YouMayLikeContents
shopNowInfo={shopNowInfo}
handleItemFocus={_handleItemFocus}
playListInfo={playListInfo && playListInfo[selectedIndex]}
version={2}
direction="horizontal"
/>
)}
</>
)}
@@ -122,6 +164,7 @@ export default function TabContainerV2({
{panelInfo?.shptmBanrTpNm === "LIVE" && playListInfo && (
<LiveChannelContents
tabTitle={tabList}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
videoVerticalVisible={videoVerticalVisible}
@@ -141,16 +184,6 @@ export default function TabContainerV2({
{tabIndex === 2 && (
<ShopNowButton onClick={onShopNowButtonClick} />
)}
{shopNowInfo && shopNowInfo.length < 3 && tabIndex === 0 && (
<YouMayLikeContents
shopNowInfo={shopNowInfo}
handleItemFocus={_handleItemFocus}
playListInfo={playListInfo && playListInfo[selectedIndex]}
version={2}
direction="horizontal"
/>
)}
</Container>
);
}

View File

@@ -9,20 +9,27 @@
// tabIndex = 0: ShopNow 스타일 (ShopNowContainer 참고)
&.tabIndex0 {
.size(@w: 100%, @h: 675px);
padding: 60px;
.size(@w: 1920px, @h: 675px);
padding: 60px 120px;
background: linear-gradient(270deg, rgba(0, 0, 0, 0.80) 0%, rgba(0, 0, 0, 0.62) 30%, rgba(0, 0, 0, 0) 65%),
linear-gradient(0deg, rgba(0, 0, 0, 0.53) 0%, rgba(20.56, 4.68, 32.71, 0.53) 60%, rgba(199, 32, 84, 0) 98%),
linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, black 45%, black 100%),
rgba(0, 0, 0, 0.56);
border-top: 1px solid rgba(234, 234, 234, 0.8);
gap: 40px;
> * {
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
}
}
// tabIndex = 1: LiveShow 스타일 (LiveShowContainer 참고)
&.tabIndex1 {
.size(@w: 100%, @h: 365px);
padding: 60px;
.size(@w: 1920px, @h: 365px);
padding: 60px 120px;
background: linear-gradient(
to top,
rgba(0, 0, 0, 0.95) 0%,
@@ -31,7 +38,14 @@
rgba(0, 0, 0, 0) 100%
);
border-top: 1px solid rgba(234, 234, 234, 0.3);
gap: 20px;
> * {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
&::before {
content: "";
@@ -58,18 +72,18 @@
&.vertical {
// vertical 모드일 때 추가 스타일 (필요시)
// 현재 추가 스타일 없음
}
}
// SHOP NOW 헤더 스타일 (ShopNowContainer 참고)
.shopNowHeader {
width: 300px;
width: 100%;
height: 70px;
padding: 20px 0;
overflow: hidden;
border-radius: 100px;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: center);
gap: 15px;
.flex(@display: flex, @justifyCenter: space-between, @alignCenter: center);
}
.shopNowIconWrapper {
@@ -85,6 +99,18 @@
object-fit: contain;
}
.shopNowHeaderLeft {
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: center);
> * {
margin-right: 15px;
&:last-child {
margin-right: 0;
}
}
}
.shopNowHeaderText {
color: #EAEAEA;
font-size: 24px;
@@ -94,6 +120,25 @@
word-wrap: break-word;
}
.closeButton {
.size(@w: 40px, @h: 40px);
.flex(@display: flex, @justifyCenter: center, @alignCenter: center);
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(234, 234, 234, 0.3);
border-radius: 50%;
color: #EAEAEA;
font-size: 24px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: rgba(0, 0, 0, 0.7);
border-color: rgba(234, 234, 234, 0.5);
color: white;
}
}
// LIVE CHANNEL 버튼 스타일 (LiveShowContainer 참고)
.liveChannelButton {
.size(@w: 300px, @h: 70px);