[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:
@@ -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 {
|
||||
// }
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
overflow: hidden;
|
||||
|
||||
> div:nth-child(1) {
|
||||
.size(@w: 100%, @h: 100%);
|
||||
width: 100%;
|
||||
height: 420px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user