[영상 관련 수정] 스타일 및 스팟 수정#1

- 상세 배경으로 영상나올때 배경처리되도록 변경.
 - 스팟 현재 처리중. 문제가 조금씩 있음.(제대로 스팟 가지않는 현상)
 - 비디오 켜졌을때 배경이 검게 보이는 현상 수정(배경 블랙부분 transparents로 변경.
  - 리스트 부분 노출  스타일변경.
This commit is contained in:
junghoon86.park
2025-10-10 17:18:51 +09:00
parent 10d96f4d8a
commit 36bd48224c
11 changed files with 195 additions and 110 deletions

View File

@@ -10,10 +10,10 @@
border: 1px solid transparent;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: flex-start, @direction: column);
flex-shrink: 0;
> * {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
@@ -60,10 +60,10 @@
.descWrap {
align-self: stretch;
.flex(@display: flex, @justifyCenter: center, @alignCenter: flex-start, @direction: column);
> * {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
@@ -83,7 +83,7 @@
.productNameTitle {
align-self: stretch;
color: #EAEAEA;
color: #eaeaea;
font-size: 24px;
font-family: @baseFont;
font-weight: 700;
@@ -97,10 +97,10 @@
align-self: stretch;
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: center);
font-weight: 700;
> * {
margin-right: 11px;
&:last-child {
margin-right: 0;
}
@@ -112,6 +112,7 @@
word-wrap: break-word;
> span {
margin-left: 10px;
color: #808080;
font-size: 24px;
font-family: @baseFont;

View File

@@ -5,14 +5,14 @@
position: relative;
width: 1114px; // ProductDetail과 동일한 고정 크기
max-width: 1114px;
height: 944px; // ProductDetail과 동일한 고정 높이
height: 740px; // ProductDetail과 동일한 고정 높이
margin-bottom: 30px; // ProductDetail과 동일한 간격
cursor: pointer;
background-color: rgba(255, 255, 255, 1);
background-color: rgba(0, 0, 0, 1);
border-radius: 12px;
box-sizing: border-box;
padding: 6px; // 포커스 테두리를 위한 공간
overflow: hidden;
.videoThumbnailWrapper {
position: relative;
// width: 658px;
@@ -26,7 +26,6 @@
overflow: hidden;
.videoThumbnail {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: none;

View File

@@ -8,7 +8,7 @@
left: 0;
width: 100%;
height: 100%;
z-index: 0; // z-index: -1 대신 0 사용
z-index: 1;
overflow: hidden;
pointer-events: none; // 클릭 이벤트가 아래로 통과하도록
}
@@ -22,7 +22,7 @@
height: 100%;
object-fit: cover; // 화면 크기에 맞춰 이미지 조정
object-position: center;
z-index: 1; // 맨 아래 레이어
z-index: 2;
}
// 그라데이션 레이어 1: 270도 방향 (왼쪽→오른쪽, 투명→불투명)
@@ -39,7 +39,7 @@
rgba(0, 0, 0, 0.77) 70%,
rgba(0, 0, 0, 1) 100%
);
z-index: 2;
z-index: 3;
}
// 그라데이션 레이어 2: 180도 방향 (위→아래, 투명→불투명)
@@ -50,8 +50,12 @@
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
z-index: 3;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 1) 100%
);
z-index: 4;
}
// 그라데이션 레이어 3: 투명 그라데이션
@@ -63,5 +67,5 @@
width: 100%;
height: 100%;
background: linear-gradient(0deg, rgba(0, 0, 0, 0));
z-index: 4;
z-index: 5;
}

View File

@@ -58,10 +58,18 @@
&.modal,
&.modal-minimized,
&.background {
/* 실제 css */
width: 1px;
height: 1px;
left: -1px;
top: -1px;
/* //실제 css */
/* 테스트 용도 */
// width: 1920px;
// height: 1080px;
// left: 0;
// top: 0;
/* //테스트 용도 */
pointer-events: none;
z-index: 1;
background-color: @videoBackgroundColor;

View File

@@ -4,38 +4,49 @@ import React, {
useMemo,
useRef,
useState,
} from "react";
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import { Job } from "@enact/core/util";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { Job } from '@enact/core/util';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import {
getContainerNode,
setContainerLastFocusedElement,
} from "@enact/spotlight/src/container";
import { pushPanel } from "../../../../actions/panelActions";
import TItemCard, { TYPES } from "../../../../components/TItemCard/TItemCard";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../hooks/useScrollTo";
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from "../../../../utils/Config";
import { scaleH } from "../../../../utils/helperMethods";
import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents";
import css1 from "./ShopNowContents.module.less";
import cssV2 from "./ShopNowContents.v2.module.less";
import { sendLogTotalRecommend } from "../../../../actions/logActions";
} from '@enact/spotlight/src/container';
import { sendLogTotalRecommend } from '../../../../actions/logActions';
import { pushPanel } from '../../../../actions/panelActions';
import TItemCard, { TYPES } from '../../../../components/TItemCard/TItemCard';
import TVirtualGridList
from '../../../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../../../hooks/useScrollTo';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from '../../../../utils/Config';
import { scaleH } from '../../../../utils/helperMethods';
import ListEmptyContents
from '../TabContents/ListEmptyContents/ListEmptyContents';
import css1 from './ShopNowContents.module.less';
import cssV2 from './ShopNowContents.v2.module.less';
const extractPriceInfo = (priceInfo) => {
if (!priceInfo) return { originalPrice: "", discountedPrice: "", discountRate: "" };
const parts = priceInfo.split("|").map(part => part.trim());
if (!priceInfo)
return { originalPrice: "", discountedPrice: "", discountRate: "" };
const parts = priceInfo.split("|").map((part) => part.trim());
return {
originalPrice: parts[0] || "",
discountedPrice: parts[1] || "",
discountRate: parts[4] || ""
discountRate: parts[4] || "",
};
};
@@ -52,12 +63,12 @@ export default function ShopNowContents({
panelInfo,
tabTitle,
version = 1,
direction = "vertical"
direction = "vertical",
}) {
const css = version === 2 ? cssV2 : css1;
const { getScrollTo, scrollTop } = useScrollTo();
const dispatch = useDispatch();
const paenls = useSelector((state) => state.panels.panels[1]?.panelInfo);
const paenls = useSelector((state) => state.panels.panels[1]?.panelInfo);
const scrollTopJob = useRef(new Job((func) => func(), 0));
const [height, setHeight] = useState();
const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]);
@@ -65,7 +76,7 @@ export default function ShopNowContents({
// 각 상품별 가격 정보를 미리 계산
const priceInfoMap = useMemo(() => {
if (!shopNowInfo) return {};
const map = {};
shopNowInfo.forEach((item, index) => {
if (item.priceInfo) {
@@ -126,15 +137,16 @@ export default function ShopNowContents({
soldoutFlag,
patncNm,
brndNm,
catNm
catNm,
} = shopNowInfo[index];
// 미리 계산된 가격 정보를 사용
const { originalPrice, discountedPrice, discountRate } = priceInfoMap[index] || {};
const { originalPrice, discountedPrice, discountRate } =
priceInfoMap[index] || {};
const handleItemClick = () => {
const params = {
tabTitle: tabTitle[tabIndex],
tabTitle: tabTitle[tabIndex],
productId: prdtId,
productTitle: prdtNm,
partner: patncNm,
@@ -144,9 +156,9 @@ export default function ShopNowContents({
category: catNm,
discount: discountRate,
contextName: LOG_CONTEXT_NAME.SHOW,
messageId: LOG_MESSAGE_ID.CONTENTCLICK
}
dispatch(sendLogTotalRecommend(params))
messageId: LOG_MESSAGE_ID.CONTENTCLICK,
};
dispatch(sendLogTotalRecommend(params));
dispatch(
pushPanel({
@@ -183,7 +195,17 @@ export default function ShopNowContents({
/>
);
},
[shopNowInfo, videoVerticalVisible, panelInfo?.shptmBanrTpNm, priceInfoMap, tabTitle, tabIndex, playListInfo, dispatch, version]
[
shopNowInfo,
videoVerticalVisible,
panelInfo?.shptmBanrTpNm,
priceInfoMap,
tabTitle,
tabIndex,
playListInfo,
dispatch,
version,
]
);
return (
@@ -196,8 +218,8 @@ export default function ShopNowContents({
dataSize={shopNowInfo.length}
direction={direction}
renderItem={renderItem}
itemWidth={version === 2 ? 310 : (videoVerticalVisible ? 540 : 600)}
itemHeight={version === 2 ? 420 : 236}
itemWidth={version === 2 ? 310 : videoVerticalVisible ? 540 : 600}
itemHeight={version === 2 ? 445 : 236}
spacing={version === 2 ? 30 : 12}
className={
videoVerticalVisible ? css.verticalItemList : css.itemList

View File

@@ -3,12 +3,12 @@
.container {
width: 100%;
height: 420px;
height: 445px;
overflow: hidden;
> div:nth-child(1) {
width: 100%;
height: 420px;
height: 445px;
}
}
@@ -21,7 +21,7 @@
> div {
> div {
> div {
.size(@w: 310px, @h: 420px);
.size(@w: 310px, @h: 445px);
> div:nth-child(1) {
flex: 1 0 auto;
}
@@ -41,7 +41,7 @@
> div {
> div {
> div {
.size(@w: 310px, @h: 420px);
.size(@w: 310px, @h: 445px);
> div:nth-child(1) {
flex: 1 0 auto;
}

View File

@@ -1,21 +1,36 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import { pushPanel } from "../../../../actions/panelActions";
import TItemCard, { TYPES } from "../../../../components/TItemCard/TItemCard";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import { LOG_MENU, panel_names } from "../../../../utils/Config";
import { $L, scaleH } from "../../../../utils/helperMethods";
import css1 from "./YouMayLikeContents.module.less";
import cssV2 from "./YouMayLikeContents.v2.module.less";
import { pushPanel } from '../../../../actions/panelActions';
import TItemCard, { TYPES } from '../../../../components/TItemCard/TItemCard';
import TVirtualGridList
from '../../../../components/TVirtualGridList/TVirtualGridList';
import {
LOG_MENU,
panel_names,
} from '../../../../utils/Config';
import {
$L,
scaleH,
} from '../../../../utils/helperMethods';
import css1 from './YouMayLikeContents.module.less';
import cssV2 from './YouMayLikeContents.v2.module.less';
export default function YouMayLikeContents({
shopNowInfo,
handleItemFocus,
playListInfo,
version = 1,
direction = "vertical"
direction = "vertical",
}) {
const css = version === 2 ? cssV2 : css1;
const dispatch = useDispatch();
@@ -90,14 +105,14 @@ export default function YouMayLikeContents({
<div className={css.title}>{$L("You may also like")}</div>
<div className={css.line}></div>
<div className={version === 2 ? css.container : ''}>
<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}
itemHeight={version === 2 ? 445 : 236}
spacing={version === 2 ? 30 : 12}
className={css.itemList}
noScrollByWheel={false}

View File

@@ -4,14 +4,14 @@ import Spottable from '@enact/spotlight/Spottable';
import css from './ShopNowButton.module.less';
const SpottableDiv = Spottable('div');
const SpottableDiv = Spottable("div");
export default function ShopNowButton({ onClick }) {
return (
<SpottableDiv className={css.container} spotlightId="shop-now-button-container">
<div className={css.shopNowButton} onClick={onClick}>
<div className={css.container} spotlightId="shop-now-button-container">
<SpottableDiv className={css.shopNowButton} onClick={onClick}>
<span className={css.buttonText}>SHOP NOW</span>
</div>
</SpottableDiv>
</SpottableDiv>
</div>
);
}

View File

@@ -10,7 +10,11 @@
width: 188px;
height: 85px;
padding: 10px 30px;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.30) 0%, rgba(0, 0, 0, 0.30) 100%);
background: linear-gradient(
0deg,
rgba(0, 0, 0, 0.3) 0%,
rgba(0, 0, 0, 0.3) 100%
);
border: 1px solid rgba(234, 234, 234, 0.5);
overflow: hidden;
border-radius: 12px;
@@ -20,7 +24,6 @@
gap: 10px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: rgba(0, 0, 0, 0.5);
border-color: rgba(234, 234, 234, 0.7);
@@ -37,9 +40,9 @@
}
.buttonText {
color: #FCFCFC;
color: #fcfcfc;
font-size: 22px;
font-family: 'LG Smart UI';
font-family: "LG Smart UI";
font-weight: 700;
line-height: 15px;
word-wrap: break-word;

View File

@@ -1,24 +1,33 @@
import React, { useCallback, useEffect } from 'react';
import React, {
useCallback,
useEffect,
} from 'react';
import classNames from 'classnames';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import icon_arrow_dwon
from '../../../../../assets/images/player/icon_tabcontainer_arrow_down.png';
import icon_shop_now
from '../../../../../assets/images/player/icon_tabcontainer_shopnow.png';
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';
import YouMayLikeContents from '../TabContents/YouMayLikeContents';
import ShopNowButton from './ShopNowButton';
import icon_arrow_dwon from '../../../../../assets/images/player/icon_tabcontainer_arrow_down.png';
import icon_shop_now from '../../../../../assets/images/player/icon_tabcontainer_shopnow.png';
import css from './TabContainer.v2.module.less';
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const SpottableDiv = Spottable('div');
const SpottableDiv = Spottable("div");
export default function TabContainerV2({
panelInfo,
@@ -39,8 +48,10 @@ export default function TabContainerV2({
tabVisible,
}) {
const tabList = [
$L('SHOP NOW'),
panelInfo?.shptmBanrTpNm === 'LIVE' ? $L('LIVE CHANNEL') : $L('FEATURED SHOWS'),
$L("SHOP NOW"),
panelInfo?.shptmBanrTpNm === "LIVE"
? $L("LIVE CHANNEL")
: $L("FEATURED SHOWS"),
];
useEffect(() => {
@@ -51,8 +62,10 @@ export default function TabContainerV2({
}
if (tabIndex === 1) {
const isLive = panelInfo?.shptmBanrTpNm === 'LIVE';
nowMenu = isLive ? LOG_MENU.FULL_LIVE_CHANNELS : LOG_MENU.FULL_FEATURED_SHOWS;
const isLive = panelInfo?.shptmBanrTpNm === "LIVE";
nowMenu = isLive
? LOG_MENU.FULL_LIVE_CHANNELS
: LOG_MENU.FULL_FEATURED_SHOWS;
}
if (nowMenu) {
@@ -74,7 +87,7 @@ export default function TabContainerV2({
if (videoVerticalVisible) {
e.stopPropagation();
e.preventDefault();
Spotlight.focus('spotlightId-video-contaienr');
Spotlight.focus("spotlightId-video-contaienr");
}
},
[videoVerticalVisible]
@@ -109,13 +122,20 @@ export default function TabContainerV2({
<div className={css.shopNowHeader}>
<div className={css.shopNowHeaderLeft}>
<div className={css.shopNowIconWrapper}>
<img src={icon_shop_now} alt="shop now icon" className={css.shopNowIcon} />
<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}>
<SpottableDiv
className={css.closeButton}
onClick={handleCloseButtonClick}
>
×
</div>
</SpottableDiv>
</div>
<ShopNowContents
tabTitle={tabList}
@@ -153,7 +173,7 @@ export default function TabContainerV2({
</div>
</SpottableDiv>
{panelInfo?.shptmBanrTpNm === 'LIVE' && playListInfo && (
{panelInfo?.shptmBanrTpNm === "LIVE" && playListInfo && (
<LiveChannelContents
tabTitle={tabList}
selectedIndex={selectedIndex}
@@ -173,9 +193,7 @@ export default function TabContainerV2({
)}
{tabVisible && tabIndex === 2 && (
<>
<ShopNowButton onClick={onShopNowButtonClick} />
</>
<ShopNowButton onClick={onShopNowButtonClick} />
)}
</Container>
);

View File

@@ -10,16 +10,27 @@
// tabIndex = 0: ShopNow 스타일 (ShopNowContainer 참고)
&.tabIndex0 {
.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);
padding: 60px;
background:
linear-gradient(
270deg,
rgba(0, 0, 0, 0.8) 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);
> * {
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
@@ -29,7 +40,7 @@
// tabIndex = 1: LiveShow 스타일 (LiveShowContainer 참고)
&.tabIndex1 {
.size(@w: 1920px, @h: 365px);
padding: 60px 120px;
padding: 60px;
background: linear-gradient(
to top,
rgba(0, 0, 0, 0.95) 0%,
@@ -38,10 +49,10 @@
rgba(0, 0, 0, 0) 100%
);
border-top: 1px solid rgba(234, 234, 234, 0.3);
> * {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
@@ -101,10 +112,10 @@
.shopNowHeaderLeft {
.flex(@display: flex, @justifyCenter: flex-start, @alignCenter: center);
> * {
margin-right: 15px;
&:last-child {
margin-right: 0;
}
@@ -112,7 +123,7 @@
}
.shopNowHeaderText {
color: #EAEAEA;
color: #eaeaea;
font-size: 24px;
font-family: @baseFont;
font-weight: 700;
@@ -126,17 +137,21 @@
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(234, 234, 234, 0.3);
border-radius: 50%;
color: #EAEAEA;
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;
}
&:focus {
background: rgba(199, 8, 80, 0.2);
border-color: @PRIMARY_COLOR_RED;
}
}
// LIVE CHANNEL 버튼 스타일 (LiveShowContainer 참고)