[HomePanel] VirtualGrid 적용

- HomeOnSale, PopularShow 부분 적용
 - 스팟부분은 아직정상적이지않아 명일 추가 확인하겠습니다.
This commit is contained in:
junghoon86.park
2024-02-05 20:58:50 +09:00
parent 8a4e54c834
commit 97303892a7
9 changed files with 363 additions and 273 deletions

View File

@@ -14,9 +14,23 @@
align-items: unset;
flex-direction: unset;
overflow: hidden;
height: 478px;
}
&.homeBestSeller {
overflow: hidden;
justify-content: unset;
}
&.onSaleItem {
margin-top: 20px;
height: 300px;
display: flex;
flex-wrap: nowrap;
overflow: hidden;
}
&.showList {
margin-top: 20px;
display: flex;
overflow: hidden;
height: 438px;
}
}

View File

@@ -1,80 +1,72 @@
import React, { useEffect } from "react";
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { VirtualGridList } from "@enact/sandstone/VirtualList";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import ri from "@enact/ui/resolution";
import { getOnSaleInfo } from "../../../actions/onSaleActions";
import TGrid from "../../../components/TGrid/TGrid";
import { $L } from "../../../utils/helperMethods";
import HomeOnSaleItem from "../HomeOnSaleItem/HomeOnSaleItem";
import css from "./HomeOnSale.module.less";
const SALE_ITEM_CONF = {
ITEM_WIDTH: 630 * 2,
ITEM_HEIGHT: 300 * 2,
SPACING: 18 * 2,
};
const Container = SpotlightContainerDecorator(
{ leaveFor: { left: "", right: "" }, enterTo: "last-focused" },
"div"
);
const SpottableComponent = Spottable("li");
const HomeOnSale = ({
isOnTop,
spotlightId,
onScrollTop,
onScrollShelf,
...rest
}) => {
const HomeOnSale = ({ ...rest }) => {
const dispatch = useDispatch();
const HomeSaleInfos = useSelector(
const homeOnSaleInfos = useSelector(
(state) => state.onSale.onSaleData.homeOnSaleInfos
);
useEffect(() => {
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: "" }));
}, []);
const renderItem = useCallback(
({ index, ...rest }) => {
const itemData = homeOnSaleInfos[index];
return (
<HomeOnSaleItem
homeOnSaleInfos={homeOnSaleInfos}
itemData={itemData}
index={index}
{...rest}
className={css.VirtualGridList}
/>
);
},
[homeOnSaleInfos]
);
return (
<Container {...rest} spotlightId={spotlightId} className={css.container}>
<Container {...rest} className={css.container}>
<div className={css.bestSeller}>
<h2 className={css.subTitle}>{$L("ON SALE")}</h2>
<ul className={css.onSaleItem}>
{HomeSaleInfos &&
HomeSaleInfos.map((item, index) => {
const priceInfo = item.priceInfo;
let salePrice;
const originalPrice = priceInfo.split("|")[0];
if (priceInfo.split("|")[3] == "") {
salePrice = priceInfo.split("|")[1];
} else {
salePrice = priceInfo.split("|")[1];
}
item.priceInfo.split("|")[0];
return (
<SpottableComponent key={index} className={css.onSaleItemList}>
<img src={item.imgUrl} className={css.onSaleItemListImg} />
<div className={css.onSaleItemListBox}>
<div className={css.onSaleItemListBoxTitle}>
{item.catNm}
</div>
<div className={css.onSaleItemListBoxSaleBox}>
up to&nbsp;
<span className={css.onSaleItemListBoxSaleBoxPer}>
{item.dcRate}%
</span>
</div>
<div className={css.onSaleItemListBoxName}>
{item.prdtNm}
</div>
<div className={css.onSaleItemListBoxPrice}>
{salePrice}
<span className={css.onSaleItemListBoxPriceStripe}>
{originalPrice}
</span>
</div>
</div>
</SpottableComponent>
);
})}
</ul>
<TGrid type="onSaleItem">
{homeOnSaleInfos && homeOnSaleInfos.length > 0 && (
<VirtualGridList
dataSize={homeOnSaleInfos.length}
direction="horizontal"
horizontalScrollbar="hidden"
itemRenderer={renderItem}
itemSize={{
minWidth: ri.scale(SALE_ITEM_CONF.ITEM_WIDTH),
minHeight: ri.scale(SALE_ITEM_CONF.ITEM_HEIGHT),
}}
noScrollByWheel
scrollMode="translate"
spacing={ri.scale(SALE_ITEM_CONF.SPACING)}
/>
)}
</TGrid>
</div>
</Container>
);

View File

@@ -13,121 +13,4 @@
font-family: "LGSmartUIBold";
font-size: 42px;
}
.onSaleItem {
margin-top: 20px;
height: 300px;
display: flex;
flex-wrap: nowrap;
overflow: hidden;
.onSaleItemList {
flex: none;
width: 630px;
height: 300px;
border-radius: 12px;
display: flex;
overflow: hidden;
margin-right: 18px;
border: 4px solid transparent;
box-sizing: border-box;
&:focus,
&:hover,
&:focus-within,
&:active {
border: 4px solid #c70850;
box-sizing: border-box;
.focusDropShadow();
}
.onSaleItemListImg {
width: 300px;
height: 300px;
object-fit: contain;
display: inline-block;
box-sizing: border-box;
}
.onSaleItemListBox {
width: 330px;
height: 300px;
box-sizing: border-box;
padding: 24px;
display: inline-block;
.onSaleItemListBoxTitle {
width: 282px;
height: 30px;
font-size: 24px;
color: rgba(255, 255, 255, 0.5);
}
.onSaleItemListBoxSaleBox {
margin-top: 11px;
width: 274px;
height: 70px;
font-size: 50px;
color: #ffffff;
text-shadow: 0px 5px 10px rgba(0, 0, 0, 0.2);
letter-spacing: -2px;
line-height: 60px;
.onSaleItemListBoxSaleBoxPer {
font-size: 84px;
letter-spacing: -3.36px;
}
}
.onSaleItemListBoxName {
margin-top: 41px;
width: 282px;
height: 60px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
font-size: 24px;
line-height: 1.33;
color: #fff;
font-weight: bold;
}
.onSaleItemListBoxPrice {
margin-top: 9px;
width: 282px;
height: 36px;
font-size: 30px;
line-height: 0.93;
color: #fff;
letter-spacing: normal;
.onSaleItemListBoxPriceStripe {
margin-left: 4px;
color: #d5d5d5;
display: inline-block;
text-decoration: line-through;
font-size: 18px;
line-height: 1.56;
letter-spacing: normal;
}
}
}
&: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

@@ -0,0 +1,61 @@
import React, { useCallback } from "react";
import Spottable from "@enact/spotlight/Spottable";
import css from "./HomeOnSaleItem.module.less";
const SpottableComponent = Spottable("div");
export default function HomeOnSaleItem({ homeOnSaleInfos, itemData, ...rest }) {
const parsePriceInfo = useCallback(
(priceInfo) => {
const priceParts = itemData.priceInfo
.split("|")
.filter((part) => part !== "N")
.map((item) => item.trim());
let originalPrice, discountedPrice, discountRate;
if (priceParts.length === 4) {
[originalPrice, discountedPrice, , discountRate] = priceParts;
} else if (priceParts.length === 2) {
[originalPrice, discountedPrice] = priceParts;
discountRate = null;
} else {
originalPrice = null;
discountedPrice = null;
discountRate = null;
}
return { originalPrice, discountedPrice, discountRate };
},
[itemData.priceInfo]
);
const { originalPrice, discountedPrice, discountRate } = parsePriceInfo(
itemData.priceInfo
);
return (
<SpottableComponent key={itemData.expsOrd} className={css.onSaleItemList}>
<img src={itemData.imgUrl} className={css.onSaleItemListImg} />
<div className={css.onSaleItemListBox}>
<div className={css.onSaleItemListBoxTitle}>{itemData.catNm}</div>
{discountRate && (
<div className={css.onSaleItemListBoxSaleBox}>
up to&nbsp;
<span className={css.onSaleItemListBoxSaleBoxPer}>
{discountRate}
</span>
</div>
)}
<div className={css.onSaleItemListBoxName}>{itemData.prdtNm}</div>
<div className={css.onSaleItemListBoxPrice}>
{discountedPrice}
{discountRate && (
<span className={css.onSaleItemListBoxPriceStripe}>
{originalPrice}
</span>
)}
</div>
</div>
</SpottableComponent>
);
}

View File

@@ -0,0 +1,110 @@
@import "../../../style/CommonStyle.module.less";
.onSaleItemList {
width: 630px;
height: 300px;
border-radius: 12px;
display: flex;
overflow: hidden;
margin-right: 18px;
border: 4px solid transparent;
box-sizing: border-box;
&:focus,
&:hover,
&:focus-within,
&:active {
border: 4px solid #c70850;
box-sizing: border-box;
.focusDropShadow();
}
.onSaleItemListImg {
width: 300px;
height: 300px;
object-fit: contain;
display: inline-block;
box-sizing: border-box;
}
.onSaleItemListBox {
width: 330px;
height: 300px;
box-sizing: border-box;
padding: 24px;
display: inline-block;
.onSaleItemListBoxTitle {
width: 282px;
height: 30px;
font-size: 24px;
color: rgba(255, 255, 255, 0.5);
}
.onSaleItemListBoxSaleBox {
margin-top: 11px;
width: 274px;
height: 70px;
font-size: 50px;
color: #ffffff;
text-shadow: 0px 5px 10px rgba(0, 0, 0, 0.2);
letter-spacing: -2px;
line-height: 60px;
.onSaleItemListBoxSaleBoxPer {
font-size: 84px;
letter-spacing: -3.36px;
}
}
.onSaleItemListBoxName {
margin-top: 41px;
width: 282px;
height: 60px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
font-size: 24px;
line-height: 1.33;
color: #fff;
font-weight: bold;
}
.onSaleItemListBoxPrice {
margin-top: 9px;
width: 282px;
height: 36px;
font-size: 30px;
line-height: 0.93;
color: #fff;
letter-spacing: normal;
.onSaleItemListBoxPriceStripe {
margin-left: 4px;
color: #d5d5d5;
display: inline-block;
text-decoration: line-through;
font-size: 18px;
line-height: 1.56;
letter-spacing: normal;
}
}
}
&: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

@@ -1,59 +1,54 @@
import React, { useEffect } from "react";
import React, { useCallback, useEffect } from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import { VirtualGridList } from "@enact/sandstone/VirtualList";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import ri from "@enact/ui/resolution";
import { getTop20Show } from "../../../actions/mainActions";
import TGrid from "../../../components/TGrid/TGrid";
import { $L } from "../../../utils/helperMethods";
import css from "../PopularShow/PopularShow.module.less";
import PopularShowItem from "../PopularShowItem/PopularShowItem";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const SpottableComponent = Spottable("li");
const PopularShow = (...rest) => {
const PopularShow = ({ ...rest }) => {
const dispatch = useDispatch();
const topInfos = useSelector((state) => state.main.top20ShowData.topInfos);
useEffect(() => {
dispatch(getTop20Show());
const renderItem = useCallback(({ index, ...rest }) => {
const itemData = topInfos[index];
return (
<PopularShowItem topInfosData={topInfos} itemNum={itemData} {...rest} />
);
}, []);
return (
<Container className={css.popularShow}>
<h2 className={css.subTitle}>{$L("POPULAR SHOW")}</h2>
<ul className={css.showList}>
{topInfos &&
topInfos.map((item) => {
return (
<SpottableComponent
key={item.expsOrd}
className={classNames(
item.thumbnailUrl960 == item.thumbnailUrl
? css.listItemVertical
: css.listItem
)}
>
<img src={item.thumbnailUrl960} className={css.itemImg} />
<div
className={classNames(
item.thumbnailUrl960 == item.thumbnailUrl
? css.verticalItem
: css.horizonItem
)}
>
{item.showNm}
</div>
</SpottableComponent>
);
})}
</ul>
<TGrid type="showList">
{topInfos && topInfos.length > 0 && (
<VirtualGridList
dataSize={topInfos.length}
direction="horizontal"
horizontalScrollbar="hidden"
itemRenderer={renderItem}
itemSize={{
minWidth: ri.scale(546 * 2),
minHeight: ri.scale(438 * 2),
}}
noScrollByWheel
scrollMode="translate"
spacing={ri.scale(18 * 2)}
/>
)}
</TGrid>
</Container>
);
};

View File

@@ -1,7 +1,7 @@
@import "../../../style/CommonStyle.module.less";
.popularShow {
padding: 60px 0 0;
width: calc(100% - 60px);
overflow: hidden;
margin-left: 60px;
.subTitle {
@@ -17,71 +17,6 @@
margin-top: 20px;
display: flex;
overflow: hidden;
> li:nth-child(1n) {
margin-right: 18px;
border: 4px solid transparent;
box-sizing: border-box;
&:focus,
&:hover,
&:focus-within,
&:active {
border: 4px solid @PRIMARY_COLOR_RED;
box-sizing: border-box;
.focusDropShadow();
}
}
.listItem {
width: 546px;
height: 438px;
padding: 18px;
background-color: @COLOR_WHITE;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
box-sizing: border-box;
> img {
width: 510px;
height: 288px;
object-fit: contain;
}
.horizonItem {
width: 510px;
height: 60px;
margin-top: 38px;
color: #333;
font-size: 24px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
.listItemVertical {
display: flex;
width: 546px;
height: 438px;
padding: 18px;
background-color: @COLOR_WHITE;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
box-sizing: border-box;
> img {
width: 228px;
height: 402px;
object-fit: contain;
}
.verticalItem {
margin-left: 11px;
color: #333;
font-size: 24px;
width: 270px;
height: 402px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 12;
}
}
height: 438px;
}
}

View File

@@ -0,0 +1,33 @@
import React from "react";
import classNames from "classnames";
import Spottable from "@enact/spotlight/Spottable";
import css from "./PopularShowItem.module.less";
const SpottableComponent = Spottable("li");
export default function PopularShowItem({ topInfosData, itemNum, ...rest }) {
console.log("###ttttt", itemNum);
return (
<SpottableComponent
className={classNames(
itemNum.thumbnailUrl960 == itemNum.thumbnailUrl
? css.listItemVertical
: css.listItem
)}
>
<img src={itemNum.thumbnailUrl960} className={css.itemImg} />
<div
className={classNames(
itemNum.thumbnailUrl960 == itemNum.thumbnailUrl
? css.verticalItem
: css.horizonItem
)}
>
{itemNum.showNm}
</div>
</SpottableComponent>
);
}

View File

@@ -0,0 +1,67 @@
@import "../../../style/CommonStyle.module.less";
.listItem {
width: 546px;
height: 438px;
padding: 18px;
background-color: @COLOR_WHITE;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
box-sizing: border-box;
> img {
width: 510px;
height: 288px;
object-fit: contain;
}
.horizonItem {
width: 510px;
height: 60px;
margin-top: 38px;
color: #333;
font-size: 24px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
.listItemVertical {
display: flex;
width: 546px;
height: 438px;
padding: 18px;
background-color: @COLOR_WHITE;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
box-sizing: border-box;
> img {
width: 228px;
height: 402px;
object-fit: contain;
}
.verticalItem {
margin-left: 11px;
color: #333;
font-size: 24px;
width: 270px;
height: 402px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 12;
}
}
li:nth-child(1n) {
margin-right: 18px;
border: 4px solid transparent;
box-sizing: border-box;
&:focus,
&:hover,
&:focus-within,
&:active {
border: 4px solid @PRIMARY_COLOR_RED;
box-sizing: border-box;
.focusDropShadow();
}
}