Merge branch 'develop' of gitlab.t-win.kr:ifheone/shoptime into develop

This commit is contained in:
sungmin.in
2024-02-06 21:35:48 +09:00
45 changed files with 1418 additions and 594 deletions

View File

@@ -6,7 +6,6 @@ import platform from "@enact/core/platform";
import ThemeDecorator from "@enact/sandstone/ThemeDecorator"; import ThemeDecorator from "@enact/sandstone/ThemeDecorator";
import appinfo from "../../webos-meta/appinfo.json"; import appinfo from "../../webos-meta/appinfo.json";
import { getBrandList } from "../actions/brandActions";
import { changeAppStatus } from "../actions/commonActions"; import { changeAppStatus } from "../actions/commonActions";
import { getAuthenticationCode } from "../actions/deviceActions"; import { getAuthenticationCode } from "../actions/deviceActions";
import { import {
@@ -59,7 +58,6 @@ function AppBase(props) {
dispatch(getHomeMenu()); dispatch(getHomeMenu());
dispatch(getHomeLayout()); dispatch(getHomeLayout());
dispatch(getHomeMainContents()); dispatch(getHomeMainContents());
dispatch(getBrandList());
dispatch(getMyRecommandedKeyword()); dispatch(getMyRecommandedKeyword());
dispatch(getBestSeller()); dispatch(getBestSeller());
dispatch( dispatch(

View File

@@ -1,10 +1,4 @@
import React, { import React, { useCallback, useRef, useState } from "react";
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import classNames from "classnames"; import classNames from "classnames";
import compose from "ramda/src/compose"; import compose from "ramda/src/compose";
@@ -25,17 +19,15 @@ const TabItemBase = ({
target, target,
deActivateTab, deActivateTab,
onClick, onClick,
lgCatCd,
isSubItem, isSubItem,
onFocus, onFocus,
path, showSubTab = false,
isArrow = false, temp,
setTemp,
...rest ...rest
}) => { }) => {
const [focused, setFocused] = useState(false); const [focused, setFocused] = useState(false);
const [pressed, setPressed] = useState(false); const [fixed, setFixed] = useState(false);
const itemRef = useRef();
const clearPressedJob = useRef( const clearPressedJob = useRef(
new Job((func) => { new Job((func) => {
setTimeout(func, 100); setTimeout(func, 100);
@@ -43,7 +35,6 @@ const TabItemBase = ({
); );
const _onClick = useCallback( const _onClick = useCallback(
(ev) => { (ev) => {
setPressed(true);
clearPressedJob.current.start(() => { clearPressedJob.current.start(() => {
if (onClick) { if (onClick) {
onClick({ index, target }); onClick({ index, target });
@@ -53,26 +44,17 @@ const TabItemBase = ({
[target, index, onClick] [target, index, onClick]
); );
useEffect(() => {}, [pressed]);
const _onFocus = useCallback(() => { const _onFocus = useCallback(() => {
setFocused(true); setFocused(true);
if (onFocus) { if (onFocus) {
onFocus(index); onFocus(index);
} }
}, [index, isArrow]); }, [index, showSubTab, onFocus]);
const _onBlur = useCallback(() => { const _onBlur = useCallback(() => {
setFocused(false); setFocused(false);
setPressed(false); }, [showSubTab]);
}, []);
const isDivider = useMemo(() => {
if (!title || !path) {
return false;
}
return true;
}, []);
const onKeyDown = useCallback( const onKeyDown = useCallback(
(event) => { (event) => {
@@ -83,30 +65,6 @@ const TabItemBase = ({
[deActivateTab] [deActivateTab]
); );
const ImageComponent = useCallback(() => {
return (
<>
<span className={classNames(css.outline)} />
<img src={path} alt="" />
</>
);
}, [path]);
const TextComponent = useCallback(() => {
return (
<>
{expanded && (
<Marquee
marqueeOn={"focus"}
className={classNames(css.text, isSubItem && css.subItem)}
>
{title}
</Marquee>
)}
</>
);
}, [title]);
const renderIcon = useCallback(() => { const renderIcon = useCallback(() => {
if (icons) { if (icons) {
const Component = icons; const Component = icons;
@@ -126,36 +84,35 @@ const TabItemBase = ({
} else { } else {
return null; return null;
} }
}, [focused, expanded, selected]); }, [focused, expanded, selected, fixed]);
delete rest.hasChildren; delete rest.hasChildren;
delete rest.getChildren; delete rest.getChildren;
return ( return (
<SpottableComponent <SpottableComponent
ref={itemRef}
className={classNames( className={classNames(
css.tabItem, css.tabItem,
!path && focused && css.focused, focused && css.focused,
!isSubItem && focused && isArrow && css.arrow, showSubTab && fixed && css.fixed,
!isSubItem && focused && showSubTab && css.arrow,
path && css.path, selected && css.selected
isSubItem && css.subDepth,
!path && !isDivider && selected && css.selected,
focused && path && css.ImgFocus,
selected && css.ImgSelect
)} )}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
onFocus={_onFocus} onFocus={_onFocus}
onBlur={_onBlur} onBlur={_onBlur}
onClick={_onClick} onClick={_onClick}
> >
<div className={css.layout}> <div className={classNames(css.itemWrap, focused && css.focused)}>
{icons && <div className={css.icon}>{renderIcon()}</div>} {icons && <div className={css.icon}>{renderIcon()}</div>}
<div className={classNames(isSubItem && css.subWrap)}> {expanded && title && (
<span className={css[`category-icon-${lgCatCd}`]} /> <Marquee
className={classNames(css.text, isSubItem && css.subItem)}
{path ? <ImageComponent /> : <TextComponent />} marqueeOn={"focus"}
</div> >
{title}
</Marquee>
)}
{/* <TabItemSub /> */}
</div> </div>
</SpottableComponent> </SpottableComponent>
); );

View File

@@ -3,11 +3,6 @@
@ICON_SIZE: 48px; @ICON_SIZE: 48px;
.spottable-next-left {
display:none;
}
.tabItem{ .tabItem{
font-size: 36px; font-size: 36px;
display: flex; display: flex;
@@ -18,9 +13,6 @@
padding-left: 42px; padding-left: 42px;
padding-right: 24px; padding-right: 24px;
// .spottable-next-left {
// background-color: red;
// }
&.focused { &.focused {
color: #eee; color: #eee;
@@ -38,70 +30,39 @@
.position(@position: absolute, @top: 24px, @right: 18px); .position(@position: absolute, @top: 24px, @right: 18px);
background-image: url('../../../assets/icons/ic-lnb-right-arrow.png'); background-image: url('../../../assets/icons/ic-lnb-right-arrow.png');
background-size: 36px 36px; background-size: 36px 36px;
} }
> div{
margin-left:-30px;
} }
} }
}
&.selected { &.selected {
color: #eee; color: #eee;
} }
&.subDepth{ &.fixed {
width: 386px; color: #eee;
height: 78px; background: linear-gradient(to right, #cb1253, #e15ba1);
padding-left: 42px; border-radius: 42px;
width: 402px;
z-index: 1;
margin-left: 30px;
position:relative;
&.arrow {
&::after {
content: "";
.size(@w:36px, @h:36px);
.position(@position: absolute, @top: 24px, @right: 18px);
background-image: url('../../../assets/icons/ic-lnb-right-arrow.png');
background-size: 36px 36px;
}
}
}
.itemWrap {
display: flex;
&.focused { &.focused {
background: rgba(255, 255, 255, .1); margin-left : -30px;
border-radius: 0px;
border-right: 6px solid #c70850;
margin-left: 0px;
> div {
padding-left: 30px;
}
}
&.path {
padding-left: 0px;
margin-bottom: 70px;
}
.outline {
.position(@position: absolute, @top: 0, @right: auto, @bottom: auto, @left: 0);
.size(@w: 138px, @h:138px);
background-position: center;
background-size: cover;
}
img {
.size(@w: 120px, @h: 120px);
}
&.ImgFocus {
.outline {
border-radius: 60px;
z-index: 99;
// box-shadow: -3px 0px 30px 0 #c70850;
// border: solid 1px #dadada;
background-image: url("../../../assets/icons/ic-tab-partners-focus@3x.png");
}
img {
.size(@w: 138px, @h: 138px);
}
}
&.ImgSelect {
img {
.size(@w: 138px, @h: 138px);
}
.outline {
background-image: url("../../../assets/icons/ic-tab-partners-lnb-selected@3x.png");
}
} }
} }
@@ -135,86 +96,13 @@
width: 245px; width: 245px;
} }
} }
.layout {
display: flex;
}
.marqueeWrap { .marqueeWrap {
width: 100%; width: 100%;
} }
} }
.subWrap{
.flex();
span {
.size(@w: 40px, @h:40px);
background-size: cover;
&.category-icon-1017 {
// LG Electronics
background-image: url("../../../assets/category/ic-category-lgelectronics-nor@3x.png");
}
// Garden and Outdoors
&.category-icon-1008 {
background-image: url("../../../assets/icons/ic-category-garden-nor.png");
}
// Fashion
&.category-icon-1000 {
background-image: url("../../../assets/icons/ic-category-fashion-nor.png");
}
// Beauty
&.category-icon-1003 {
background-image: url("../../../assets/icons/ic-category-beauty-nor.png");
}
// Jewelry
&.category-icon-1004 {
background-image: url("../../../assets/icons/ic-category-jewelry-nor.png");
}
// Home
&.category-icon-1006 {
background-image: url("../../../assets/icons/ic-category-home-nor.png");
}
// Kitchen & Food
&.category-icon-1007 {
background-image: url("../../../assets/icons/ic-category-kitchen-nor.png");
}
// Accessories
&.category-icon-1014 {
background-image: url("../../../assets/icons/ic-category-accessories-nor.png");
}
// Heaclth & Fitness
&.category-icon-1009 {
background-image: url("../../../assets/icons/ic-category-health-nor.png");
}
// Entertainment
&.category-icon-1012 {
background-image: url("../../../assets/icons/ic-category-enter-nor.png");
}
// Crafts & Sewing
&.category-icon-1011 {
background-image: url("../../../assets/icons/ic-category-cw-nor.png");
}
// Electronics
&.category-icon-1010 {
background-image: url("../../../assets/icons/ic-category-electronics-nor.png");
}
// Clearance
&.category-icon-1013 {
background-image: url("../../../assets/icons/ic-category-clearance-nor.png");
}
}
}

View File

@@ -0,0 +1,126 @@
import React, { useCallback, useRef, useState } from "react";
import classNames from "classnames";
import compose from "ramda/src/compose";
import { Job } from "@enact/core/util";
import { Marquee, MarqueeController } from "@enact/sandstone/Marquee";
import Spottable from "@enact/spotlight/Spottable";
import css from "./TabItemSub.module.less";
const SpottableComponent = Spottable("div");
const TabItemBase = ({
title,
expanded = false,
selected = false,
index = 0,
target,
deActivateTab,
onClick,
lgCatCd,
isSubItem,
onFocus,
onBlur,
path,
subTitle,
...rest
}) => {
const [focused, setFocused] = useState(false);
const itemRef = useRef();
const clearPressedJob = useRef(
new Job((func) => {
setTimeout(func, 100);
}, 100)
);
const _onClick = useCallback(
(ev) => {
clearPressedJob.current.start(() => {
if (onClick) {
onClick({ index, target });
}
});
},
[target, index, onClick]
);
const _onFocus = useCallback(() => {
setFocused(true);
if (onFocus) {
onFocus(index);
}
}, [index]);
const _onBlur = useCallback(() => {
setFocused(false);
if (onBlur) {
onBlur(index);
}
}, [index]);
const onKeyDown = useCallback(
(event) => {
if (event.key === "ArrowRight") {
_onClick();
}
},
[deActivateTab]
);
const ImageComponent = useCallback(() => {
return (
<div className={css.imageLayout}>
<span className={classNames(css.outline)} />
<img src={path} alt="" />
</div>
);
}, [path]);
const TextComponent = useCallback(() => {
return (
<>
{expanded && (
<Marquee
marqueeOn={"focus"}
className={classNames(css.text, isSubItem && css.subItem)}
>
{title}
</Marquee>
)}
</>
);
}, [title]);
delete rest.hasChildren;
delete rest.getChildren;
return (
<SpottableComponent
ref={itemRef}
className={classNames(
css.tabItem,
!path && focused && css.focused,
path && css.path
// focused && path && css.imageFocus,
// selected && css.imageSelect
)}
onKeyDown={onKeyDown}
onFocus={_onFocus}
onBlur={_onBlur}
onClick={_onClick}
>
<div className={css.layout}>
<div className={classNames(isSubItem && css.subWrap)}>
{!path && <span className={css[`category-icon-${lgCatCd}`]} />}
{path ? <ImageComponent /> : <TextComponent />}
</div>
</div>
</SpottableComponent>
);
};
const ItemDecorator = compose(MarqueeController({ marqueeOnFocus: true }));
const TabItemSub = ItemDecorator(TabItemBase);
export default TabItemSub;

View File

@@ -0,0 +1,166 @@
@import "../../style/utils.module.less";
@import "../../style/CommonStyle.module.less";
@ICON_SIZE: 48px;
.tabItem{
font-size: 36px;
display: flex;
color: #606060;
align-items: center;
height: 84px;
position: relative;
padding-left: 42px;
padding-right: 24px;
&.focused {
background: rgba(255, 255, 255, .1);
border-radius: 0px;
border-right: 6px solid #c70850;
margin-left: 0px;
color: #eee;
}
&.path {
padding-left: 0px;
margin-bottom: 70px;
}
&.selected {
color: #eee;
}
.imageLayout {
&.outline {
.position(@position: absolute, @top: 0, @right: auto, @bottom: auto, @left: 0);
.size(@w: 138px, @h:138px);
background-position: center;
background-size: cover;
> img {
.size(@w: 120px, @h: 120px);
}
}
&.imageFocus {
.outline {
.focusDropShadow();
border-radius: 60px;
z-index: 99;
background-image: url("../../../assets/icons/ic-tab-partners-focus@3x.png");
// box-shadow: -3px 0px 30px 0 #c70850;
// border: solid 1px #dadada;
> img {
.size(@w: 138px, @h: 138px);
}
}
}
// &.ImgSelect {
// img {
// .size(@w: 138px, @h: 138px);
// }
// .outline {
// background-image: url("../../../assets/icons/ic-tab-partners-lnb-selected@3x.png");
// }
// }
}
.text {
line-height: 1.2;
padding-left: 11px;
.font(@fontFamily:@baseFontBold, @fontSize:36px);
&.subItem {
.font (@fontFamily:@baseFontBold, @fontSize:30px);
width: 245px;
}
}
.subWrap{
.flex();
span {
.size(@w: 40px, @h:40px);
background-size: cover;
&.category-icon-1017 {
// LG Electronics
background-image: url("../../../assets/category/ic-category-lgelectronics-nor@3x.png");
}
// Garden and Outdoors
&.category-icon-1008 {
background-image: url("../../../assets/icons/ic-category-garden-nor.png");
}
// Fashion
&.category-icon-1000 {
background-image: url("../../../assets/icons/ic-category-fashion-nor.png");
}
// Beauty
&.category-icon-1003 {
background-image: url("../../../assets/icons/ic-category-beauty-nor.png");
}
// Jewelry
&.category-icon-1004 {
background-image: url("../../../assets/icons/ic-category-jewelry-nor.png");
}
// Home
&.category-icon-1006 {
background-image: url("../../../assets/icons/ic-category-home-nor.png");
}
// Kitchen & Food
&.category-icon-1007 {
background-image: url("../../../assets/icons/ic-category-kitchen-nor.png");
}
// Accessories
&.category-icon-1014 {
background-image: url("../../../assets/icons/ic-category-accessories-nor.png");
}
// Heaclth & Fitness
&.category-icon-1009 {
background-image: url("../../../assets/icons/ic-category-health-nor.png");
}
// Entertainment
&.category-icon-1012 {
background-image: url("../../../assets/icons/ic-category-enter-nor.png");
}
// Crafts & Sewing
&.category-icon-1011 {
background-image: url("../../../assets/icons/ic-category-cw-nor.png");
}
// Electronics
&.category-icon-1010 {
background-image: url("../../../assets/icons/ic-category-electronics-nor.png");
}
// Clearance
&.category-icon-1013 {
background-image: url("../../../assets/icons/ic-category-clearance-nor.png");
}
}
}
.layout {
display: flex;
}
.marqueeWrap {
width: 100%;
}
}

View File

@@ -32,6 +32,7 @@ import OnSaleIcon from "./iconComponents/OnSaleIcon";
import SearchIcon from "./iconComponents/SearchIcon"; import SearchIcon from "./iconComponents/SearchIcon";
import TrendingNowIcon from "./iconComponents/TrendingNowIcon"; import TrendingNowIcon from "./iconComponents/TrendingNowIcon";
import TabItem from "./TabItem"; import TabItem from "./TabItem";
import TabItemSub from "./TabItemSub";
import css from "./TabLayout.module.less"; import css from "./TabLayout.module.less";
const Container = SpotlightContainerDecorator( const Container = SpotlightContainerDecorator(
@@ -113,7 +114,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
const [mainExpanded, setMainExpanded] = useState(false); const [mainExpanded, setMainExpanded] = useState(false);
const [mainSelectedIndex, setMainSelectedIndex] = useState(-1); const [mainSelectedIndex, setMainSelectedIndex] = useState(-1);
const [secondDepthReduce, setSecondDepthReduce] = useState(false); const [secondDepthReduce, setSecondDepthReduce] = useState(false);
const [focused, setFocused] = useState(false); const [temp, setTemp] = useState(false);
const [tabs, setTabs] = useState([]); const [tabs, setTabs] = useState([]);
const [tabFocused, setTabFocused] = useState([false, false, false]); //COLLABSED_MAIN, ACTIVATED_MAIN, ACTIVATED_SUB const [tabFocused, setTabFocused] = useState([false, false, false]); //COLLABSED_MAIN, ACTIVATED_MAIN, ACTIVATED_SUB
const panelSwitching = useRef(null); const panelSwitching = useRef(null);
@@ -167,6 +168,16 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
result = data?.homeCategory.map((item) => ({ result = data?.homeCategory.map((item) => ({
id: item.lgCatCd, id: item.lgCatCd,
title: item.lgCatNm, title: item.lgCatNm,
target: [
{
name: panel_names.CATEGORY_PANEL,
panelInfo: {
lgCatNm: item.lgCatNm,
lgCatCd: item.lgCatCd,
COUNT: item.COUNT,
},
},
],
})); }));
break; break;
case "GNB": case "GNB":
@@ -179,7 +190,12 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
result = data?.shortFeaturedBrands.map((item) => ({ result = data?.shortFeaturedBrands.map((item) => ({
Id: item.patnrId, Id: item.patnrId,
path: item.patncLogoPath, path: item.patncLogoPath,
target: [{ name: panel_names.FEATURED_BRANDS_PANEL }], target: [
{
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: item.patnrId,
},
],
})); }));
break; break;
case "My Page": case "My Page":
@@ -309,9 +325,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
switch (type) { switch (type) {
case ACTIVATED_MAIN: { case ACTIVATED_MAIN: {
if (!cursorVisible) { if (!cursorVisible) {
// setMainExpanded(false);
} }
break; break;
} }
case ACTIVATED_SUB: { case ACTIVATED_SUB: {
@@ -336,16 +350,16 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
); );
const onFocus = (index) => { const onFocus = (index) => {
setFocused(true);
setMainSelectedIndex(index); setMainSelectedIndex(index);
setTemp(false);
if (showSubTab) { if (showSubTab) {
setSecondDepthReduce((prev) => !prev); setSecondDepthReduce((prev) => !prev);
} }
}; };
const onBlur = () => { const onBlur = useCallback(() => {
setFocused(false); setTemp(true);
}; }, []);
const handleNavigation = useCallback( const handleNavigation = useCallback(
({ index, target }) => { ({ index, target }) => {
@@ -365,6 +379,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
({ index, target }) => { ({ index, target }) => {
if (target) { if (target) {
dispatch(resetPanels(target)); dispatch(resetPanels(target));
deActivateTab(); deActivateTab();
panelSwitching.current = true; panelSwitching.current = true;
panelSwitchingJob.start(panelSwitching); panelSwitchingJob.start(panelSwitching);
@@ -376,7 +391,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
({ index, target }) => { ({ index, target }) => {
deActivateTabJob.startAfter(100, deActivateTab); deActivateTabJob.startAfter(100, deActivateTab);
}, },
[dispatch, deActivateTab] [deActivateTab]
); );
const tabActivated = useMemo(() => { const tabActivated = useMemo(() => {
@@ -433,7 +448,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
} }
}); });
} }
}, [secondDepthReduce, showSubTab, focused]); }, [secondDepthReduce, showSubTab]);
if (!showTab) { if (!showTab) {
return null; return null;
@@ -485,13 +500,14 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
<TabItem <TabItem
{...item} {...item}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur}
key={"tabitemExpanded" + index} key={"tabitemExpanded" + index}
onClick={handleNavigation} onClick={handleNavigation}
deActivateTab={deActivateTab} deActivateTab={deActivateTab}
isArrow={showSubTab} showSubTab={showSubTab}
icons={item.icons} icons={item.icons}
expanded={mainExpanded} expanded={mainExpanded}
temp={temp}
setTemp={setTemp}
selected={ selected={
(panels.length === 0 && index === 0) || (panels.length === 0 && index === 0) ||
(Array.isArray(item.target) && (Array.isArray(item.target) &&
@@ -521,8 +537,9 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
{showSubTab && {showSubTab &&
tabs[mainSelectedIndex]?.children.map((item, index) => { tabs[mainSelectedIndex]?.children.map((item, index) => {
return ( return (
<TabItem <TabItemSub
{...item} {...item}
onBlur={onBlur}
key={"tabitemSubmenu" + index} key={"tabitemSubmenu" + index}
onClick={onClickSubItem} onClick={onClickSubItem}
expanded={true} expanded={true}
@@ -532,7 +549,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
title={item.title} title={item.title}
lgCatCd={item.id} lgCatCd={item.id}
path={item.path} path={item.path}
isArrow={showSubTab} showSubTab={showSubTab}
// selected={ // selected={
// (panels.length === 0 && index === 0) || // (panels.length === 0 && index === 0) ||
// (Array.isArray(item.target) && // (Array.isArray(item.target) &&

View File

@@ -39,6 +39,7 @@
justify-content: center; justify-content: center;
z-index: 1; z-index: 1;
flex-grow: 0; flex-grow: 0;
// transition: width 0.5s ease; // transition: width 0.5s ease;
> img { > img {
@@ -47,6 +48,7 @@
margin: -40px 24px 84px 42px; margin: -40px 24px 84px 42px;
} }
&.expanded { &.expanded {
width: 402px; width: 402px;
@@ -54,6 +56,7 @@
width: 234px; width: 234px;
height: 54px; height: 54px;
} }
} }
&.secondDepthLayout { &.secondDepthLayout {

View File

@@ -2,9 +2,9 @@ import React from "react";
import css from "./Banner.module.less"; import css from "./Banner.module.less";
export default function Banner({ selectedBrandInfo, brandTopImgInfo }) { export default function Banner({ brandTopImgInfo, selectedBrandInfo }) {
const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo;
const { topImgAlt, topImgPath } = brandTopImgInfo; const { topImgAlt, topImgPath } = brandTopImgInfo;
const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo;
return ( return (
<div className={css.container}> <div className={css.container}>

View File

@@ -1,11 +1,15 @@
import React, { useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import Spotlight from "@enact/spotlight";
import { import {
getBrandLayoutInfo, getBrandLayoutInfo,
getBrandList,
getBrandLiveChannelInfo, getBrandLiveChannelInfo,
} from "../../actions/brandActions"; } from "../../actions/brandActions";
import TBody from "../../components/TBody/TBody";
import TPanel from "../../components/TPanel/TPanel"; import TPanel from "../../components/TPanel/TPanel";
import Banner from "./Banner/Banner"; import Banner from "./Banner/Banner";
import css from "./FeaturedBrandsPanel.module.less"; import css from "./FeaturedBrandsPanel.module.less";
@@ -19,12 +23,11 @@ const getSelectedBrandInfo = (brandInfo, selectedPatnrId) => {
export default function FeaturedBrandsPanel() { export default function FeaturedBrandsPanel() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const panelInfo = useSelector((state) => state.panels.panels[0].panelInfo);
const brandInfo = useSelector((state) => state.brand.brandInfoData.brandInfo); const brandInfo = useSelector((state) => state.brand.brandInfoData.brandInfo);
const brandTopImgInfo = useSelector( const brandTopImgInfo = useSelector(
(state) => state.brand.brandLayoutInfoData.brandTopImgInfo (state) => state.brand.brandLayoutInfoData.brandTopImgInfo
); );
const brandChanInfo = useSelector( const brandChanInfo = useSelector(
(state) => state.brand.brandLiveChannelInfoData.brandChanInfo (state) => state.brand.brandLiveChannelInfoData.brandChanInfo
); );
@@ -32,39 +35,57 @@ export default function FeaturedBrandsPanel() {
(state) => state.brand.brandLiveChannelInfoData.brandChannelCnt (state) => state.brand.brandLiveChannelInfoData.brandChannelCnt
); );
const [selectedPatnrId, setSelectedPatnrId] = useState(String(panelInfo));
const [selectedBrandInfo, setSelectedBrandInfo] = useState(); const [selectedBrandInfo, setSelectedBrandInfo] = useState();
// @@pyh Todo, provided by GNB as props or global state const handleQuickMenuClick = useCallback(
const [selectedPatnrId, setSelectedPatnrId] = useState("1"); (patnrId) => {
if (selectedPatnrId === patnrId) {
const handleQuickMenu = (patnrId) => { return;
setSelectedPatnrId(patnrId);
};
useEffect(() => {
if (brandInfo) {
setSelectedBrandInfo(getSelectedBrandInfo(brandInfo, selectedPatnrId));
} }
setSelectedPatnrId(patnrId);
},
[selectedPatnrId]
);
useEffect(() => {
if (!brandInfo) {
dispatch(getBrandList());
}
}, [brandInfo, dispatch]);
useEffect(() => {
if (brandInfo && selectedPatnrId) {
setSelectedBrandInfo(getSelectedBrandInfo(brandInfo, selectedPatnrId));
dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId })); dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandLiveChannelInfo({ patnrId: selectedPatnrId })); dispatch(getBrandLiveChannelInfo({ patnrId: selectedPatnrId }));
}
}, [brandInfo, dispatch, selectedPatnrId]);
useEffect(() => {
// @@pyh Todo, loading 적용 이후 loading으로 조건 추가
if (selectedPatnrId) {
Spotlight.focus("spotlight" + selectedPatnrId);
}
}, [selectedPatnrId]); }, [selectedPatnrId]);
return ( return (
/* scenario page 98 */ /* scenario page 98 */
<TPanel className={css.container}> <TPanel className={css.container}>
<TBody>
{brandInfo && brandInfo.length > 1 && ( {brandInfo && brandInfo.length > 1 && (
<QuickMenu <QuickMenu
brandInfo={brandInfo} brandInfo={brandInfo}
onQuickMenuClick={handleQuickMenu} onQuickMenuClick={handleQuickMenuClick}
selectedPatnrId={selectedPatnrId} selectedPatnrId={selectedPatnrId}
/> />
)} )}
{selectedBrandInfo && ( {brandTopImgInfo && selectedBrandInfo && (
<Banner <Banner
selectedBrandInfo={selectedBrandInfo}
brandTopImgInfo={brandTopImgInfo} brandTopImgInfo={brandTopImgInfo}
selectedBrandInfo={selectedBrandInfo}
/> />
)} )}
@@ -76,6 +97,7 @@ export default function FeaturedBrandsPanel() {
/> />
)} )}
</div> </div>
</TBody>
</TPanel> </TPanel>
); );
} }

View File

@@ -1,4 +1,7 @@
.container { .container {
.tBody {
height: 1080px;
}
.sectionContainer { .sectionContainer {
padding-left: 60px; padding-left: 60px;
} }

View File

@@ -1,6 +1,7 @@
import React from "react"; import React, { useEffect } from "react";
import Scroller from "@enact/sandstone/Scroller"; import { VirtualGridList } from "@enact/sandstone/VirtualList";
import ri from "@enact/ui/resolution";
import SectionTitle from "../../../components/SectionTitle/SectionTitle"; import SectionTitle from "../../../components/SectionTitle/SectionTitle";
import TItemCard from "../../../components/TItemCard/TItemCard"; import TItemCard from "../../../components/TItemCard/TItemCard";
@@ -8,8 +9,39 @@ import { $L } from "../../../utils/helperMethods";
import LiveVideoCard from "../LiveVideoCard/LiveVideoCard"; import LiveVideoCard from "../LiveVideoCard/LiveVideoCard";
import css from "./LiveChannels.module.less"; import css from "./LiveChannels.module.less";
// getBrandLiveChannelInfo → brandChanInfo → brandProductInfo
// brandProductInfo = {
// freeShippingFlag = null, // 무료배송 여부
// offerInfo = "", // 제공 정보
// prdtId,
// prdtImgUrl,
// prdtNm,
// priceInfo, // 할인 전 금액, 할인 후(최종) 금액, 리워드여부, save금액, off(할인 %)
// revwGrd = null, 리뷰 등급 (별점)
// soldoutFlag,
// }
/*
<TItemCard
key={prdtId}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
soldoutFlag={soldoutFlag}
type="horizontal"
/>
*/
const LIVE_CHANNELS_STRING = "LIVE CHANNELS"; const LIVE_CHANNELS_STRING = "LIVE CHANNELS";
const LIST_ITEM_CONF = {
ITEM_WIDTH: 660 * 2,
ITEM_HEIGHT: 236 * 2,
SAPCING: 12 * 2,
};
export default function LiveChannels({ brandChanInfo, brandChannelCnt }) { export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
const { const {
alamDispFlag, alamDispFlag,
@@ -33,30 +65,27 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
vtctpYn, // 영상 세로 여부 vtctpYn, // 영상 세로 여부
} = brandChanInfo; } = brandChanInfo;
// getOnSaleInfos → saleInfos → saleProductInfos console.log();
// saleProductInfos = {
// imgUrl,
// lgCatCd,
// lgCatNm,
// OfferInfo = null,
// patncNm,
// patnrId,
// prdtNm,
// priceInfo
// }
// 현재 OnSalePanel 실사용 = imgUrl, prdtId, prdtNm, priceInfo
// getBrandLiveChannelInfo → brandChanInfo → brandProductInfo const renderItem = ({ index, ...rest }) => {
// brandProductInfo = { const { prdtImgUrl, prdtId, prdtNm, priceInfo, soldoutFlag } =
// freeShippingFlag = null, // 무료배송 여부 brandProductInfo[index];
// offerInfo = "", // 제공 정보 return (
// prdtId, <>
// prdtImgUrl, <TItemCard
// prdtNm, key={prdtId}
// priceInfo, // 할인 전 금액, 할인 후(최종) 금액, 리워드여부, save금액, off(할인 %) imageAlt={prdtNm}
// revwGrd = null, 리뷰 등급 (별점) imageSource={prdtImgUrl}
// soldoutFlag, priceInfo={priceInfo}
// } productId={prdtId}
productName={prdtNm}
{...rest}
soldoutFlag={soldoutFlag}
type="horizontal"
/>
</>
);
};
return ( return (
<> <>
@@ -73,69 +102,31 @@ export default function LiveChannels({ brandChanInfo, brandChannelCnt }) {
thumbnailSource={thumbnailImgPath} thumbnailSource={thumbnailImgPath}
title={showNm} title={showNm}
/> />
<Scroller
className={css.scroller} <div className={css.tempContainer}>
{brandProductInfo && (
<VirtualGridList
className={css.virtualGridList}
dataSize={brandProductInfo.length}
direction="vertical" direction="vertical"
noScrollByWheel={true} itemRenderer={renderItem}
itemSize={{
minWidth: ri.scale(LIST_ITEM_CONF.ITEM_WIDTH),
minHeight: ri.scale(LIST_ITEM_CONF.ITEM_HEIGHT),
}}
noScrollByWheel
scrollMode="translate" scrollMode="translate"
spacing={ri.scale(LIST_ITEM_CONF.SAPCING)}
verticalScrollbar="hidden" verticalScrollbar="hidden"
>
<ul>
{brandProductInfo &&
brandProductInfo.map(
({
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
soldoutFlag,
}) => {
return (
<TItemCard
key={prdtId}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
soldoutFlag={soldoutFlag}
type="horizontal"
/> />
);
}
)} )}
</ul> </div>
</Scroller>
</div> </div>
)} )}
{brandChannelCnt > 1 && ( {brandChannelCnt > 1 && (
<div> <div>
{"brandChannelCnt(영상의 수)가 1보다 클 경우, type = vertical"} {"brandChannelCnt(영상의 수)가 1보다 클 경우, type = vertical"}
<ul>
{brandProductInfo &&
brandProductInfo.map(
({
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
soldoutFlag,
}) => {
return (
<TItemCard
key={prdtId}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
soldoutFlag={soldoutFlag}
/>
);
}
)}
</ul>
</div> </div>
)} )}
</div> </div>

View File

@@ -2,25 +2,25 @@
@import "../../../style/utils.module.less"; @import "../../../style/utils.module.less";
.container { .container {
margin-bottom: 58px;
h2 { h2 {
margin: 60px 0 24px; margin: 58px 0 24px;
} }
/* normal */
> div { > div {
position: relative; position: relative;
display: flex; .flex(@justifyCenter: flex-start);
width: @globalWidth; width: 100%;
// product scroll container .tempContainer {
> .scroller { .size(@w: 660px, @h: 564px);
width: 720px; .virtualGridList {
// overflow: unset;
ul { > div {
display: flex; // overflow: unset !important;
flex-direction: column; }
gap: 12px;
height: 564px;
} }
} }
} }

View File

@@ -54,13 +54,7 @@ export default memo(function LiveVideoCard({
<div> <div>
<div> <div>
<img <img src={IcLiveShow} alt={$L(LIVE_SHOW_STRING)} />
src={IcLiveShow}
alt={
// @@pyh Todo, alt 값 언어처리 유무
LIVE_SHOW_STRING
}
/>
<div> <div>
<h3>{title}</h3> <h3>{title}</h3>
<time> <time>
@@ -68,15 +62,11 @@ export default memo(function LiveVideoCard({
</time> </time>
</div> </div>
</div> </div>
{liveChannelCount && liveChannelCount === 1 && ( {liveChannelCount && liveChannelCount === 1 && (
// @@pyh, live 방송이라는 조건으로 변경
<div> <div>
<img <img src={IcWarning} alt={$L(WARNING_STRING)} />
src={IcWarning}
alt={
// @@pyh Todo, alt 값 언어처리 유무
WARNING_STRING
}
/>
<span>{$L(WARNING_MESSAGE)}</span> <span>{$L(WARNING_MESSAGE)}</span>
</div> </div>
)} )}

View File

@@ -7,7 +7,6 @@
z-index: 10; z-index: 10;
.size(@w: 1002px, @h: 564px); .size(@w: 1002px, @h: 564px);
margin-right: 18px; margin-right: 18px;
border: 4px solid transparent;
border-radius: 12px; border-radius: 12px;
overflow: hidden; overflow: hidden;
@@ -23,7 +22,7 @@
.size(@w: 100%, @h: 100%); .size(@w: 100%, @h: 100%);
background-image: linear-gradient(to top, transparent 55%, @COLOR_BLACK); background-image: linear-gradient(to top, transparent 55%, @COLOR_BLACK);
// top part of video // top part (live info)
> div:nth-child(1) { > div:nth-child(1) {
.flex(@justifyCenter: flex-start); .flex(@justifyCenter: flex-start);
gap: 12px; gap: 12px;
@@ -48,7 +47,7 @@
} }
} }
// bottom part of video // bottom part (warning message)
> div:nth-child(2) { > div:nth-child(2) {
.flex(@justifyCenter: flex-start); .flex(@justifyCenter: flex-start);
gap: 12px; gap: 12px;
@@ -66,7 +65,9 @@
/* focused */ /* focused */
&:focus-within { &:focus-within {
border: 4px solid @PRIMARY_COLOR_RED; &::after {
.focused(@borderRadius: 12px);
}
.focusDropShadow(); .focusDropShadow();
} }
} }

View File

@@ -1,64 +1,61 @@
import React, { useEffect } from 'react'; import React, { useCallback, useEffect } from "react";
import Spotlight from '@enact/spotlight'; import { VirtualGridList } from "@enact/sandstone/VirtualList";
import SpotlightContainerDecorator import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
from '@enact/spotlight/SpotlightContainerDecorator'; import ri from "@enact/ui/resolution";
import Spottable from '@enact/spotlight/Spottable';
import { $L } from '../../../utils/helperMethods'; import QuickMenuItem from "../QuickMenuItem/QuickMenuItem";
import css from './QuickMenu.module.less'; import css from "./QuickMenu.module.less";
const LIST_ITEM_CONF = {
ITEM_WIDTH: 138 * 2,
ITEM_HEIGHT: 138 * 2,
};
const Container = SpotlightContainerDecorator( const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: "last-focused" }, { leaveFor: { right: "" }, enterTo: "last-focused" },
"nav" "nav"
); );
const SpottableComponent = Spottable("li");
export default function QuickMenu({ export default function QuickMenu({
brandInfo, brandInfo,
selectedPatnrId, selectedPatnrId,
onQuickMenuClick, onQuickMenuClick,
...rest ...rest
}) { }) {
const handleClick = (patnrId) => { const renderItem = useCallback(
if (selectedPatnrId === patnrId) { ({ index, ...rest }) => {
return;
}
onQuickMenuClick(patnrId);
};
useEffect(() => {
console.log("@@ brandInfo", brandInfo);
// @@pyh Todo, change to logic
// const element = document.getElementById(selectedPatnrId);
Spotlight.focus("spotlight" + selectedPatnrId);
}, []);
return ( return (
<Container {...rest} className={css.container}> <QuickMenuItem
<ul> brandInfo={brandInfo}
{brandInfo && index={index}
brandInfo.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => { selectedPatnrId={selectedPatnrId}
return ( onQuickMenuClick={onQuickMenuClick}
<SpottableComponent {...rest}
className={patnrId === selectedPatnrId && css.selected} />
id={patnrId}
key={patnrId}
onClick={() => handleClick(patnrId)}
spotlightId={"spotlight" + patnrId}
>
{newFlag === "Y" && (
<span className={css.newBagde}>{$L("NEW")}</span>
)}
<span className={css.outline} />
<img src={logoImgPath} alt={logoImgAlt} />
</SpottableComponent>
); );
})} },
</ul> [brandInfo, selectedPatnrId, onQuickMenuClick]
);
return (
<Container className={css.container} {...rest}>
{brandInfo && (
<VirtualGridList
className={css.virtualGridList}
dataSize={brandInfo.length}
direction="horizontal"
horizontalScrollbar="hidden"
itemRenderer={renderItem}
itemSize={{
minWidth: ri.scale(LIST_ITEM_CONF.ITEM_WIDTH),
minHeight: ri.scale(LIST_ITEM_CONF.ITEM_HEIGHT),
}}
noScrollByWheel
scrollMode="translate"
// spacing={ri.scale(LIST_ITEM_CONF.SAPCING)}
/>
)}
</Container> </Container>
); );
} }

View File

@@ -2,66 +2,15 @@
@import "../../../style/utils.module.less"; @import "../../../style/utils.module.less";
.container { .container {
.flex(@justifyCenter:flex start);
.size(@w: 100%, @h: 180px); .size(@w: 100%, @h: 180px);
padding: 30px 60px; padding: 18px 60px;
background-color: #e7e7e7; background-color: #e7e7e7;
ul { .virtualGridList {
.flex(@justifyCenter: flex-start); overflow: unset;
gap: 18px;
/* normal */ > div {
li { overflow: unset !important;
position: relative;
.newBagde {
.position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: auto);
z-index: 10;
.size(@w: 60px, @h: 30px);
background-color: #f00;
color: #fff;
border-radius: 6px;
font-family: Arial;
font-weight: 600;
font-size: 18px;
text-align: center;
}
.outline {
.position(@position: absolute, @top: 0, @right: auto, @bottom: auto, @left: 0);
.size(@w: 138px, @h:138px);
background-position: center;
background-size: cover;
}
img {
.size(@w: 120px, @h: 120px);
}
/* focused */
&:focus {
border-radius: 60px;
.focusDropShadow();
.outline {
background-image: url("../../../../assets/images/partners/ic-tab-partners-focus@3x.png");
}
img {
.size(@w: 138px, @h: 138px);
}
}
/* selected */
&.selected {
img {
.size(@w: 138px, @h: 138px);
}
.outline {
background-image: url("../../../../assets/images/partners/ic-tab-partners-selected@3x.png");
}
}
} }
} }
} }

View File

@@ -0,0 +1,45 @@
import React, { memo, useCallback } from "react";
import classNames from "classnames";
import Spottable from "@enact/spotlight/Spottable";
import { $L } from "../../../utils/helperMethods";
import css from "./QuickMenuItem.module.less";
const SpottableComponent = Spottable("div");
export default memo(function QuickMenuItem({
brandInfo,
index,
onQuickMenuClick,
selectedPatnrId,
...rest
}) {
const { logoImgAlt, logoImgPath, newFlag, patnrId } = brandInfo[index];
const handleClick = useCallback(
(patnrId) => {
onQuickMenuClick && onQuickMenuClick(patnrId);
},
[selectedPatnrId]
);
return (
<SpottableComponent
className={classNames(
css.item,
patnrId === selectedPatnrId && css.selected
)}
id={patnrId}
key={patnrId}
onClick={() => handleClick(patnrId)}
{...rest}
spotlightId={"spotlight" + patnrId}
>
<img src={logoImgPath} alt={logoImgAlt} />
{newFlag === "Y" && <div>{$L("NEW")}</div>}
<span />
</SpottableComponent>
);
});

View File

@@ -0,0 +1,73 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.item {
/* normal */
position: relative;
.size(@w: 138px, @h: 138px);
.flex();
// partner logo
img {
.size(@w: 120px, @h: 120px);
}
// newBadge
> div {
.position(@position: absolute, @top: 0, @right: 18px);
z-index: 10;
.size(@w: 60px, @h: 30px);
border-radius: 6px;
background-color: #f00;
.font(@fontFamily: @arialFontBold, @fontSize: 18px);
color: @COLOR_WHITE;
text-align: center;
}
// outline
> span {
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
.size(@w: 138px, @h:138px);
background-position: center;
background-size: cover;
}
/* focused */
&:focus-within {
border-radius: 100%;
.focusDropShadow();
// partner logo
img {
.size(@w: 138px, @h: 138px);
}
// newBadge
> div {
right: 0;
}
// outline
> span {
background-image: url("../../../../assets/images/partners/ic-tab-partners-focus@3x.png");
}
}
/* selected */
&.selected {
// partner logo
img {
.size(@w: 138px, @h: 138px);
}
// newBadge
> div {
right: 0;
}
// outline
> span {
background-image: url("../../../../assets/images/partners/ic-tab-partners-selected@3x.png");
}
}
}

View File

@@ -0,0 +1,6 @@
{
"main": "QuickMenuItem.jsx",
"styles": [
"QuickMenuItem.module.less"
]
}

View File

@@ -48,7 +48,7 @@ const HomeOnSale = ({ ...rest }) => {
<Container {...rest} className={css.container}> <Container {...rest} className={css.container}>
<div className={css.bestSeller}> <div className={css.bestSeller}>
<h2 className={css.subTitle}>{$L("ON SALE")}</h2> <h2 className={css.subTitle}>{$L("ON SALE")}</h2>
<TGrid type="onSaleItem"> <div className={css.onSaleItem}>
{homeOnSaleInfos && homeOnSaleInfos.length > 0 && ( {homeOnSaleInfos && homeOnSaleInfos.length > 0 && (
<VirtualGridList <VirtualGridList
className={css.grid} className={css.grid}
@@ -65,7 +65,7 @@ const HomeOnSale = ({ ...rest }) => {
spacing={ri.scale(SALE_ITEM_CONF.SPACING)} spacing={ri.scale(SALE_ITEM_CONF.SPACING)}
/> />
)} )}
</TGrid> </div>
</div> </div>
</Container> </Container>
); );

View File

@@ -16,5 +16,85 @@
.grid { .grid {
height: 300px; height: 300px;
> div {
> div {
> div {
&:nth-child(5n + 1) {
> div {
> div {
background: linear-gradient(to top, #f485c3, #cc4d92);
}
}
}
&:nth-child(5n + 2) {
> div {
> div {
background: linear-gradient(to top, #fdbe43, #e47915);
}
}
}
&:nth-child(5n + 3) {
> div {
> div {
background: linear-gradient(to top, #97ca73, #3e8d18);
}
}
}
&:nth-child(5n + 4) {
> div {
> div {
background: linear-gradient(to top, #84b0e9, #4282d9);
}
}
}
&:nth-child(5n + 5) {
> div {
> div {
background: linear-gradient(to top, #a387ea, #7750dc);
}
}
}
}
}
}
}
&.onSaleItem {
margin-top: 20px;
height: 300px;
display: flex;
flex-wrap: nowrap;
overflow: hidden;
// > div {
// > div {
// > div {
// > div:nth-child(5n + 1) {
// .onSaleItemListBox {
// background-image: linear-gradient(to top, #f485c3, #cc4d92);
// }
// }
// > div:nth-child(5n + 2) {
// .onSaleItemListBox {
// background-image: linear-gradient(to top, #fdbe43, #e47915);
// }
// }
// > div:nth-child(5n + 3) {
// .onSaleItemListBox {
// background-image: linear-gradient(to top, #97ca73, #3e8d18);
// }
// }
// > div:nth-child(5n + 4) {
// .onSaleItemListBox {
// background-image: linear-gradient(to top, #84b0e9, #4282d9);
// }
// }
// > div:nth-child(5n + 5) {
// .onSaleItemListBox {
// background-image: linear-gradient(to top, #a387ea, #7750dc);
// }
// }
// }
// }
// }
} }
} }

View File

@@ -16,6 +16,7 @@
box-sizing: border-box; box-sizing: border-box;
.focusDropShadow(); .focusDropShadow();
} }
.onSaleItemListImg { .onSaleItemListImg {
width: 300px; width: 300px;
height: 300px; height: 300px;
@@ -29,6 +30,7 @@
box-sizing: border-box; box-sizing: border-box;
padding: 24px; padding: 24px;
display: inline-block; display: inline-block;
.onSaleItemListBoxTitle { .onSaleItemListBoxTitle {
width: 282px; width: 282px;
height: 30px; height: 30px;
@@ -82,29 +84,4 @@
} }
} }
} }
&:nth-child(5n + 1) {
.onSaleItemListBox {
background-image: linear-gradient(to top, #f485c3, #cc4d92);
}
}
&:nth-child(5n + 2) {
.onSaleItemListBox {
background-image: linear-gradient(to top, #fdbe43, #e47915);
}
}
&:nth-child(5n + 3) {
.onSaleItemListBox {
background-image: linear-gradient(to top, #97ca73, #3e8d18);
}
}
&:nth-child(5n + 4) {
.onSaleItemListBox {
background-image: linear-gradient(to top, #84b0e9, #4282d9);
}
}
&:nth-child(5n + 5) {
.onSaleItemListBox {
background-image: linear-gradient(to top, #a387ea, #7750dc);
}
}
} }

View File

@@ -4,6 +4,7 @@
.hotPicks { .hotPicks {
height: 100%; height: 100%;
position: relative; position: relative;
> div { > div {
height: 100%; height: 100%;
} }
@@ -19,27 +20,16 @@
.sectionWrap { .sectionWrap {
height: 100%; height: 100%;
position: relative; position: relative;
padding: 90px 30px;
> div { > div {
height: 100%; height: 100%;
.flex(); .flex();
> img { img {
width: auto; width: auto;
max-width: 100%; max-width: 1740px;
height: auto; height: auto;
position: relative; position: relative;
&:focus-within {
&::after {
width: 100%;
height: 100%;
box-sizing: border-box;
border: 4px solid @PRIMARY_COLOR_RED;
position: absolute;
left: 0;
top: 0;
content: "";
}
}
} }
} }
> ul { > ul {
@@ -49,14 +39,7 @@
position: relative; position: relative;
&:focus-within { &:focus-within {
&::after { &::after {
width: 100%; .focused();
height: 100%;
box-sizing: border-box;
border: 4px solid @PRIMARY_COLOR_RED;
position: absolute;
left: 0;
top: 0;
content: "";
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import React, {
} from 'react'; } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { data } from 'ilib';
import { import {
useDispatch, useDispatch,
useSelector, useSelector,
@@ -16,9 +17,16 @@ import { getThemeCurationInfo } from '../../actions/homeActions';
import TBody from '../../components/TBody/TBody'; import TBody from '../../components/TBody/TBody';
import TPanel from '../../components/TPanel/TPanel'; import TPanel from '../../components/TPanel/TPanel';
import css from './HotPicks.module.less'; import css from './HotPicks.module.less';
import TCFI from './Type/TCFI/TCFI'; // Full Image import TCAD from './Type/TCAD/TCAD';
import TCFV_2 from './Type/TCFV_2/TCFV_2'; // Full Image + VOD + Item(2) import TCFI from './Type/TCFI/TCFI';
import TCHH from './Type/TCHH/TCHH'; // Half Image X2 import TCFI_2 from './Type/TCFI_2/TCFI_2';
import TCFI_3 from './Type/TCFI_3/TCFI_3';
import TCFI_4 from './Type/TCFI_4/TCFI_4';
import TCFV from './Type/TCFV/TCFV';
import TCFV_2 from './Type/TCFV_2/TCFV_2';
import TCFV_3 from './Type/TCFV_3/TCFV_3';
import TCFV_4 from './Type/TCFV_4/TCFV_4';
import TCHH from './Type/TCHH/TCHH';
const SpottableComponent = Spottable("button"); const SpottableComponent = Spottable("button");
@@ -33,11 +41,11 @@ export default function HotPicksPanel() {
: []; : [];
const [currentPage, setCurrentPage] = useState(0); const [currentPage, setCurrentPage] = useState(0);
const currentType = [...themeCurationType][currentPage]; const currentType = themeCurationType[currentPage];
const currentTypeData = const currentTypeData =
themeCurationInfoData && themeCurationInfoData &&
themeCurationInfoData.filter((item) => item.tmpltCd === currentType); themeCurationInfoData.filter((item) => item.tmpltCd === currentType);
console.log("##themeCurationInfoData", themeCurationInfoData);
const handlePrev = useCallback(() => { const handlePrev = useCallback(() => {
setCurrentPage((prevIndex) => setCurrentPage((prevIndex) =>
prevIndex === 0 ? themeCurationType.length - 1 : prevIndex - 1 prevIndex === 0 ? themeCurationType.length - 1 : prevIndex - 1
@@ -50,46 +58,59 @@ export default function HotPicksPanel() {
); );
}, [currentPage]); }, [currentPage]);
// TCFI : Full Image console.log("##themeCurationInfoData", themeCurationInfoData);
// TCHH : Half Image X2
// TCFI_2 : Full Image + Item(2)
// TCFI_3 : Full Image + Item(3)
// TCFI_4 : Full Image + Item(4)
// TCFV : Full Image + VOD
// TCFV_2 : Full Image + VOD + Item(2)
// TCFV_3 : Full Image + VOD + Item(3)
// TCFV_4 : Full Image + VOD + Item(4)
// TCAD : Theme AD
let typeComponent; let typeComponent;
switch (currentType) { switch (currentType) {
case "TCFI": case "TCFI": // Full Image
typeComponent = ( typeComponent = (
<TCFI data={currentTypeData} className={css.sectionWrap} /> <TCFI data={currentTypeData} className={css.sectionWrap} />
); );
break; break;
case "TCFV_2": case "TCHH": // Half Image X2
typeComponent = (
<TCFV_2 data={currentTypeData} className={css.sectionWrap} />
);
break;
case "TCHH":
typeComponent = ( typeComponent = (
<TCHH data={currentTypeData} className={css.sectionWrap} /> <TCHH data={currentTypeData} className={css.sectionWrap} />
); );
break; break;
case "TCFI_4": case "TCFI_2": // Full Image + Item(2)
typeComponent = (
<TCFI_2 data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCFI_2": case "TCFI_3": // Full Image + Item(3)
typeComponent = (
<TCFI_3 data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCFI_3": case "TCFI_4": // Full Image + Item(4)
typeComponent = (
<TCFI_4 data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCFV": case "TCFV": // Full Image + VOD
typeComponent = (
<TCFV data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCFV_4": case "TCFV_2": // Full Image + VOD + Item(2)
typeComponent = (
<TCFV_2 data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCAD": case "TCFV_3": // Full Image + VOD + Item(3)
typeComponent = (
<TCFV_3 data={currentTypeData} className={css.sectionWrap} />
);
break; break;
case "TCFV_3": case "TCFV_4": // Full Image + VOD + Item(4)
typeComponent = (
<TCFV_4 data={currentTypeData} className={css.sectionWrap} />
);
break;
case "TCAD": // Theme AD
typeComponent = (
<TCAD data={currentTypeData} className={css.sectionWrap} />
);
break; break;
default: default:
typeComponent = ( typeComponent = (

View File

@@ -0,0 +1,14 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCAD.module.less';
const SpottableComponent = Spottable("div");
export default function TCAD({ className, data }) {
console.log("##data", data);
return <>Full 영상</>;
}

View File

@@ -1,7 +1,8 @@
import React from 'react'; import React, { useEffect } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Spotlight from '@enact/spotlight';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
import css from './TCFI.module.less'; import css from './TCFI.module.less';
@@ -9,18 +10,27 @@ import css from './TCFI.module.less';
const SpottableComponent = Spottable("div"); const SpottableComponent = Spottable("div");
export default function TCFI({ className, data }) { export default function TCFI({ className, data }) {
console.log("##data", data); useEffect(() => {
Spotlight.focus("spotData");
// if (data) {
// const showInfos = data[0]?.showInfos[0];
// const id = showInfos.showId;
// Spotlight.focus("spotlight" + id);
// }
}, [data]);
return ( return (
<> <>
{data && {data &&
data.map(({ bgImgPath, curationNm, tmpltCd }) => { data.map(({ bgImgPath, curationNm, tmpltCd }) => {
return ( return (
<div <div key={tmpltCd} className={classNames(className && className)}>
key={tmpltCd} <SpottableComponent
className={classNames(className ? className : "")} className={css.fullImg}
spotlightId="spotData"
> >
<SpottableComponent className={css.fullImg}> <p>
<img src={bgImgPath} alt={curationNm} /> <img src={bgImgPath} alt={curationNm} />
</p>
</SpottableComponent> </SpottableComponent>
</div> </div>
); );

View File

@@ -0,0 +1,13 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.fullImg {
&:focus-within {
> p {
position: relative;
&::after {
.focused();
}
}
}
}

View File

@@ -0,0 +1,37 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCFI_2.module.less';
const SpottableComponent = Spottable("li");
export default function TCFI_2({ className, data }) {
console.log("##data", data);
return (
<>
{data &&
data.map(({ bgImgPath, curationNm, tmpltCd, productInfos }) => {
return (
<div key={tmpltCd} className={classNames(className && className)}>
<div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} />
</div>
<ul className={css.itemList}>
{productInfos &&
productInfos.slice(0, 2).map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</ul>
</div>
);
})}
</>
);
}

View File

@@ -0,0 +1,15 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.itemList {
width: 100%;
justify-content: center;
left: 0;
bottom: 190px;
> li {
margin: 0 20px;
> img {
.size(@w:340px, @h:340px);
}
}
}

View File

@@ -0,0 +1,37 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCFI_3.module.less';
const SpottableComponent = Spottable("li");
export default function TCFI_3({ className, data }) {
console.log("##data", data);
return (
<>
{data &&
data.map(({ bgImgPath, curationNm, tmpltCd, productInfos }) => {
return (
<div key={tmpltCd} className={classNames(className && className)}>
<div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} />
</div>
<ul className={css.itemList}>
{productInfos &&
productInfos.slice(0, 3).map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</ul>
</div>
);
})}
</>
);
}

View File

@@ -0,0 +1,15 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.itemList {
width: 100%;
justify-content: center;
left: 0;
bottom: 190px;
> li {
margin: 0 20px;
> img {
.size(@w:340px, @h:340px);
}
}
}

View File

@@ -0,0 +1,63 @@
import React, { 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 css from './TCFI_4.module.less';
const SpottableComponent = Spottable("li");
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveId: true },
"ul"
);
export default function TCFI_4({ className, data }) {
useEffect(() => {
if (data) {
const productInfos = data[0]?.productInfos[0];
const id = productInfos.prdtId;
Spotlight.focus("spotlight" + id);
}
}, [data]);
return (
<>
{data &&
data.map(({ bgImgPath, curationNm, tmpltCd, productInfos }) => {
return (
<div key={tmpltCd} className={classNames(className && className)}>
<div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} />
</div>
<Container className={css.itemList}>
{productInfos &&
productInfos.slice(0, 2).map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent
key={prdtId}
spotlightId={"spotlight" + prdtId}
>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</Container>
<Container className={css.itemList}>
{productInfos &&
productInfos.slice(2, 4).map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</Container>
</div>
);
})}
</>
);
}

View File

@@ -0,0 +1,15 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.itemList {
width: 524px;
right: 120px;
bottom: 180px;
flex-wrap: wrap;
> li {
margin: 10px;
> img {
.size(@w:242px, @h:242px);
}
}
}

View File

@@ -0,0 +1,49 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCFV.module.less';
const SpottableComponent = Spottable("li");
export default function TCFV({ className, data }) {
console.log("##data", data);
return (
<>
{data &&
data.map(({ bgImgPath, curationNm, tmpltCd, showInfos }) => {
return (
<div key={tmpltCd} className={classNames(className && className)}>
<div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} />
</div>
<ul className={css.itemList}>
{showInfos &&
showInfos
.slice(0, 1)
.map(
({
dfltThumbnailImgPath,
showId,
showNm,
showUrl,
thumbnailUrl,
}) => {
// @@@ 영상 출력 처리
return (
<SpottableComponent key={showId}>
<img src={thumbnailUrl} alt={showNm} />
{/* <video src={showUrl}></video> */}
</SpottableComponent>
);
}
)}
</ul>
</div>
);
})}
</>
);
}

View File

@@ -0,0 +1,15 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.itemList {
right: 180px;
bottom: 298px;
> li {
> img {
.size(@w:860px, @h:484px);
}
> video {
.size(@w:860px, @h:484px);
}
}
}

View File

@@ -1,40 +1,82 @@
import React from 'react'; import React, { useEffect } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
import css from './TCFV_2.module.less'; import css from './TCFV_2.module.less';
const SpottableComponent = Spottable("li"); const SpottableComponent = Spottable("li");
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveId: true },
"ul"
);
export default function TCFV_2({ className, data }) { export default function TCFV_2({ className, data }) {
console.log("##data", data); useEffect(() => {
if (data) {
const showInfos = data[0]?.showInfos[0];
const id = showInfos.showId;
Spotlight.focus("spotlight" + id);
}
}, [data]);
return ( return (
<> <>
{data && {data &&
data.map(({ bgImgPath, curationNm, tmpltCd, productInfos }) => { data.map(
({ bgImgPath, curationNm, tmpltCd, productInfos, showInfos }) => {
return ( return (
<div <div key={tmpltCd} className={classNames(className && className)}>
key={tmpltCd}
className={classNames(className ? className : "")}
>
<div className={css.fullImg}> <div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} /> <img src={bgImgPath} alt={curationNm} />
</div> </div>
<ul className={css.itemList}> <Container className={css.itemList}>
{showInfos &&
showInfos
.slice(0, 1)
.map(
({
dfltThumbnailImgPath,
showId,
showNm,
showUrl,
thumbnailUrl,
}) => {
// @@@ 영상 출력 처리
return (
<SpottableComponent
key={showId}
spotlightId={"spotlight" + showId}
>
<img
src={thumbnailUrl}
alt={showNm}
className={css.video}
/>
{/* <video src={showUrl}></video> */}
</SpottableComponent>
);
}
)}
{productInfos && {productInfos &&
productInfos.slice(0, 2).map(({ imgUrl, prdtNm, prdtId }) => { productInfos
.slice(0, 2)
.map(({ imgUrl, prdtNm, prdtId }) => {
return ( return (
<SpottableComponent key={prdtId}> <SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} /> <img src={imgUrl} alt={prdtNm} />
</SpottableComponent> </SpottableComponent>
); );
})} })}
</ul> </Container>
</div> </div>
); );
})} }
)}
</> </>
); );
} }

View File

@@ -9,5 +9,9 @@
> img { > img {
.size(@w:242px, @h:242px); .size(@w:242px, @h:242px);
} }
> video,
.video {
.size(@w:430px, @h:242px);
}
} }
} }

View File

@@ -0,0 +1,67 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCFV_3.module.less';
const SpottableComponent = Spottable("li");
export default function TCFV_3({ className, data }) {
console.log("##data", data);
return (
<>
{data &&
data.map(
({ bgImgPath, curationNm, tmpltCd, productInfos, showInfos }) => {
return (
<div
key={tmpltCd}
className={classNames(className && className, css.flexCont)}
>
<div className={css.fullImg}>
{showInfos &&
showInfos
.slice(0, 1)
.map(
({
dfltThumbnailImgPath,
showId,
showNm,
showUrl,
thumbnailUrl,
}) => {
// @@@ 영상 출력 처리
return (
<SpottableComponent key={showId}>
<img
src={thumbnailUrl}
alt={showNm}
className={css.video}
/>
{/* <video src={showUrl}></video> */}
</SpottableComponent>
);
}
)}
</div>
<ul className={css.itemList}>
{productInfos &&
productInfos
.slice(0, 4)
.map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</ul>
</div>
);
}
)}
</>
);
}

View File

@@ -0,0 +1,22 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.flexCont {
padding: 135px 50px;
display: flex;
justify-content: space-between;
> div {
width: 1414px;
}
}
.itemList {
width: 240px;
position: relative !important;
flex-direction: column;
justify-content: space-between;
> li {
> img {
.size(@w:240px, @h:240px);
}
}
}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import classNames from 'classnames';
import Spottable from '@enact/spotlight/Spottable';
import css from './TCFV_4.module.less';
const SpottableComponent = Spottable("li");
export default function TCFV_4({ className, data }) {
console.log("##data", data);
return (
<>
{data &&
data.map(
({ bgImgPath, curationNm, tmpltCd, productInfos, showInfos }) => {
return (
<div key={tmpltCd} className={classNames(className && className)}>
<div className={css.fullImg}>
<img src={bgImgPath} alt={curationNm} />
</div>
<ul className={css.itemList}>
{showInfos &&
showInfos
.slice(0, 1)
.map(
({
dfltThumbnailImgPath,
showId,
showNm,
showUrl,
thumbnailUrl,
}) => {
// @@@ 영상 출력 처리
return (
<SpottableComponent key={showId}>
<img
src={thumbnailUrl}
alt={showNm}
className={css.video}
/>
{/* <video src={showUrl}></video> */}
</SpottableComponent>
);
}
)}
{productInfos &&
productInfos
.slice(0, 4)
.map(({ imgUrl, prdtNm, prdtId }) => {
return (
<SpottableComponent key={prdtId}>
<img src={imgUrl} alt={prdtNm} />
</SpottableComponent>
);
})}
</ul>
</div>
);
}
)}
</>
);
}

View File

@@ -0,0 +1,19 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.itemList {
width: 100%;
justify-content: center;
left: 0;
bottom: 200px;
> li {
margin: 10px;
> img {
.size(@w:242px, @h:242px);
}
> video,
.video {
.size(@w:430px, @h:242px);
}
}
}

View File

@@ -1,15 +1,26 @@
import React from 'react'; import React, { useEffect } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
import css from './TCHH.module.less'; import css from './TCHH.module.less';
const SpottableComponent = Spottable("div"); const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveId: true },
"div"
);
export default function TCHH({ className, data }) { export default function TCHH({ className, data }) {
console.log("##data", data); useEffect(() => {
if (data) {
Spotlight.focus("spotlight0");
}
}, [data]);
return ( return (
<> <>
{data && {data &&
@@ -17,16 +28,23 @@ export default function TCHH({ className, data }) {
return ( return (
<div <div
key={tmpltCd} key={tmpltCd}
className={classNames(className ? className : "", css.halfWrap)} className={classNames(className && className, css.halfWrap)}
> >
<Container>
{bgImgPath && {bgImgPath &&
bgImgPath.slice(0, 2).map((bgImgPath, idx) => { bgImgPath.slice(0, 2).map((bgImgPath, idx) => {
return ( return (
<SpottableComponent key={idx}> <SpottableComponent
key={idx}
spotlightId={"spotlight" + idx}
>
<p>
<img src={bgImgPath} alt={curationNm} /> <img src={bgImgPath} alt={curationNm} />
</p>
</SpottableComponent> </SpottableComponent>
); );
})} })}
</Container>
</div> </div>
); );
})} })}

View File

@@ -2,8 +2,20 @@
@import "../../../../style/utils.module.less"; @import "../../../../style/utils.module.less";
.halfWrap { .halfWrap {
> div {
display: flex; display: flex;
> div { > div {
display: flex;
justify-content: center;
flex: 1; flex: 1;
&:focus-within {
> p {
position: relative;
&::after {
.focused();
}
}
}
}
} }
} }

View File

@@ -45,8 +45,6 @@ export default function OnSalePanel() {
dispatch( dispatch(
getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentCategoryCode }) getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentCategoryCode })
); );
} else {
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: "" }));
} }
}, [currentCategoryCode, dispatch]); }, [currentCategoryCode, dispatch]);