[CategoryPanel] 카테고리 패널 작업 진행 중 / TODO: ITEM 탭 구현 및 데이터 백그라운드 페칭
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import ItemContents from "./ItemContents/ItemContents";
|
||||||
|
import ShowContents from "./ShowContents/ShowContents";
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { left: "", right: "" }, enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function CategoryContents({ activeTab }) {
|
||||||
|
const datas = useSelector((state) => state.main.subCategoryData);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{activeTab === "SHOW" ? <ShowContents /> : <ItemContents />}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function ItemContents() {
|
||||||
|
return <p>ITEM</p>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import css from "./ShowContents.module.less";
|
||||||
|
import ShowLists from "./ShowLists/ShowLists";
|
||||||
|
import ShowProductContents from "./ShowProductContents/ShowProductContents";
|
||||||
|
|
||||||
|
const SpottableVideoContainer = Spottable("div");
|
||||||
|
|
||||||
|
export default function ShowContents() {
|
||||||
|
const showContentDatas = useSelector((state) => state.main.subCategoryData);
|
||||||
|
const { categoryShowInfos, partnerInfos, topShowInfo } =
|
||||||
|
showContentDatas || {};
|
||||||
|
|
||||||
|
console.log("showContentDatas", showContentDatas);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showContentDatas &&
|
||||||
|
topShowInfo &&
|
||||||
|
categoryShowInfos &&
|
||||||
|
categoryShowInfos.length > 0 && (
|
||||||
|
<>
|
||||||
|
<h1 className={css.topsTitle}>{topShowInfo.showNm}</h1>
|
||||||
|
<div className={css.topShowContainer}>
|
||||||
|
<SpottableVideoContainer className={css.videoContainer}>
|
||||||
|
<img src={topShowInfo.thumbnailUrl} alt={topShowInfo.showNm} />
|
||||||
|
</SpottableVideoContainer>
|
||||||
|
<ShowProductContents />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ShowLists />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.topsTitle {
|
||||||
|
margin: 70px 0 26px;
|
||||||
|
color: @COLOR_GRAY08;
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 36px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.topShowContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.videoContainer {
|
||||||
|
.size(@w: 754px, @h: 438px);
|
||||||
|
border: 1px solid @COLOR_GRAY02;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 11px 18px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
outline: 4px solid @PRIMARY_COLOR_RED;
|
||||||
|
outline-offset: -3px;
|
||||||
|
box-shadow: inset 0 0 0 4px @PRIMARY_COLOR_RED,
|
||||||
|
0 0 50px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import React, { useCallback } from "react";
|
||||||
|
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import { VirtualGridList } from "@enact/sandstone/VirtualList";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import { scaleH, scaleW } from "../../../../../utils/helperMethods";
|
||||||
|
import css from "./ShowLists.module.less";
|
||||||
|
import ShowListsItem from "./ShowListsItem";
|
||||||
|
|
||||||
|
const LIST_ITEM_CONF = {
|
||||||
|
ITEM_WIDTH: 546,
|
||||||
|
ITEM_HEIGHT: 438,
|
||||||
|
SPACING: 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function ShowLists() {
|
||||||
|
const showListDatas = useSelector(
|
||||||
|
(state) => state.main.subCategoryData?.categoryShowInfos
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({ index, ...rest }) => {
|
||||||
|
const ItemInfo = showListDatas[index];
|
||||||
|
const {
|
||||||
|
catNm,
|
||||||
|
dfltThumbnailImgPath,
|
||||||
|
hstNm,
|
||||||
|
lgCatCd,
|
||||||
|
pathnLogoPath,
|
||||||
|
patnrId,
|
||||||
|
showId,
|
||||||
|
showNm,
|
||||||
|
thumbnailUrl,
|
||||||
|
total,
|
||||||
|
vtctpYn,
|
||||||
|
} = ItemInfo;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ShowListsItem
|
||||||
|
key={showId}
|
||||||
|
thumbnail={thumbnailUrl}
|
||||||
|
title={showNm}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[showListDatas]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("showListDatas", showListDatas);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={css.container}>
|
||||||
|
{showListDatas && showListDatas.length > 0 && (
|
||||||
|
<VirtualGridList
|
||||||
|
className={css.grid}
|
||||||
|
dataSize={showListDatas.length}
|
||||||
|
direction="vertical"
|
||||||
|
verticalScrollbar="hidden"
|
||||||
|
itemRenderer={renderItem}
|
||||||
|
itemSize={{
|
||||||
|
minWidth: scaleW(LIST_ITEM_CONF.ITEM_WIDTH),
|
||||||
|
minHeight: scaleH(LIST_ITEM_CONF.ITEM_HEIGHT),
|
||||||
|
}}
|
||||||
|
noScrollByWheel
|
||||||
|
scrollMode="translate"
|
||||||
|
spacing={scaleW(LIST_ITEM_CONF.SPACING)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@import "../../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
.size(@w: 100%, @h: auto);
|
||||||
|
margin-top: 60px;
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
overflow: unset;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
overflow: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import css from "./ShowListsItem.module.less";
|
||||||
|
|
||||||
|
const ShowListsItemContainer = Spottable("div");
|
||||||
|
|
||||||
|
export default function ShowListsItem({ thumbnail, title }) {
|
||||||
|
return (
|
||||||
|
<ShowListsItemContainer className={css.container}>
|
||||||
|
<div className={css.imgWrap}>
|
||||||
|
<img src={thumbnail} alt={title} />
|
||||||
|
</div>
|
||||||
|
<div className={css.titleWrap}>
|
||||||
|
<p className={css.title}>{title}</p>
|
||||||
|
</div>
|
||||||
|
</ShowListsItemContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@import "../../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
.size(@w: 100%, @h: 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid @COLOR_GRAY02;
|
||||||
|
background-color: @COLOR_WHITE;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
.imgWrap {
|
||||||
|
.size(@w: 100%, @h: 288px);
|
||||||
|
|
||||||
|
> img {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleWrap {
|
||||||
|
.size(@w: 100%, @h: calc(100% - 288px));
|
||||||
|
display: flex;
|
||||||
|
padding-top: 18px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 24px);
|
||||||
|
color: @COLOR_GRAY06;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
&::after {
|
||||||
|
.focused(@boxShadow: 50px, @borderRadius:12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useCallback } from "react";
|
||||||
|
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import { VirtualGridList } from "@enact/sandstone/VirtualList";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import TItemCard from "../../../../../components/TItemCard/TItemCard";
|
||||||
|
import { scaleH, scaleW } from "../../../../../utils/helperMethods";
|
||||||
|
import css from "./ShowProductContents.module.less";
|
||||||
|
|
||||||
|
const LIST_ITEM_CONF = {
|
||||||
|
ITEM_WIDTH: 324,
|
||||||
|
ITEM_HEIGHT: 438,
|
||||||
|
SPACING: 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function ShowProductContents() {
|
||||||
|
const showProductContentDatas = useSelector(
|
||||||
|
(state) => state.main.subCategoryData?.topShowInfo
|
||||||
|
);
|
||||||
|
const { productInfos } = showProductContentDatas || {};
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({ index, ...rest }) => {
|
||||||
|
const productInfo = productInfos[index];
|
||||||
|
const { imgUrl, prdtId, prdtNm, priceInfo } = productInfo;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TItemCard
|
||||||
|
{...rest}
|
||||||
|
key={prdtId}
|
||||||
|
imageAlt={prdtId}
|
||||||
|
imageSource={imgUrl}
|
||||||
|
priceInfo={priceInfo}
|
||||||
|
productName={prdtNm}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[productInfos]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={css.container}>
|
||||||
|
{productInfos && productInfos.length > 0 && (
|
||||||
|
<VirtualGridList
|
||||||
|
classname={css.grid}
|
||||||
|
dataSize={productInfos.length}
|
||||||
|
direction="horizontal"
|
||||||
|
horizontalScrollbar="hidden"
|
||||||
|
itemRenderer={renderItem}
|
||||||
|
itemSize={{
|
||||||
|
minWidth: scaleW(LIST_ITEM_CONF.ITEM_WIDTH),
|
||||||
|
minHeight: scaleH(LIST_ITEM_CONF.ITEM_HEIGHT),
|
||||||
|
}}
|
||||||
|
noScrollByWheel
|
||||||
|
scrollMode="translate"
|
||||||
|
spacing={scaleW(LIST_ITEM_CONF.SPACING)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
@import "../../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
.flex();
|
||||||
|
width: calc(100% - 790px);
|
||||||
|
margin: 0 18px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
height: 438px;
|
||||||
|
overflow: unset;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
overflow: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,150 @@
|
|||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
|
||||||
|
import { getSubCategory } from "../../actions/mainActions";
|
||||||
|
import SectionTitle from "../../components/SectionTitle/SectionTitle";
|
||||||
|
import TBody from "../../components/TBody/TBody";
|
||||||
|
import TButtonTab, { LIST_TYPE } from "../../components/TButtonTab/TButtonTab";
|
||||||
|
import TDropDown from "../../components/TDropDown/TDropDown";
|
||||||
|
import THeader from "../../components/THeader/THeader";
|
||||||
import TPanel from "../../components/TPanel/TPanel";
|
import TPanel from "../../components/TPanel/TPanel";
|
||||||
|
import { $L } from "../../utils/helperMethods";
|
||||||
|
import ItemContents from "./CategoryContents/ItemContents/ItemContents";
|
||||||
|
import ShowContents from "./CategoryContents/ShowContents/ShowContents";
|
||||||
|
import css from "./CategoryPanel.module.less";
|
||||||
|
|
||||||
|
const getButtonTabList = () => {
|
||||||
|
return [$L("SHOW"), $L("ITEM")];
|
||||||
|
};
|
||||||
|
|
||||||
|
let buttonTabList = null;
|
||||||
|
|
||||||
export default function CategoryPanel() {
|
export default function CategoryPanel() {
|
||||||
return <TPanel>Category</TPanel>;
|
if (!buttonTabList) {
|
||||||
|
buttonTabList = getButtonTabList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const categoryDatas = useSelector((state) => state.main.subCategoryData);
|
||||||
|
const panelInfos = useSelector((state) => state.panels.panels?.[0]);
|
||||||
|
|
||||||
|
const { lgCatCd, lgCatNm, COUNT } = panelInfos.panelInfo || {};
|
||||||
|
const {
|
||||||
|
bannerInfos,
|
||||||
|
categoryDescInfos,
|
||||||
|
categoryFilterCd,
|
||||||
|
categoryItemInfos,
|
||||||
|
categoryShowInfos,
|
||||||
|
categoryTabTypeCd,
|
||||||
|
partnerInfos,
|
||||||
|
topShowInfo,
|
||||||
|
} = categoryDatas || {};
|
||||||
|
|
||||||
|
const [tab, setTab] = useState(0);
|
||||||
|
const [dropDownTab, setDropDownTab] = useState(0);
|
||||||
|
const [filterMethods, setFilterMethods] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTab(0);
|
||||||
|
setDropDownTab(0);
|
||||||
|
|
||||||
|
if (panelInfos?.panelInfo) {
|
||||||
|
dispatch(getSubCategory({ lgCatCd, filterType: "CAT00202" }));
|
||||||
|
}
|
||||||
|
}, [panelInfos, lgCatCd, dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (categoryDatas) {
|
||||||
|
const detailCdNmValues = categoryFilterCd
|
||||||
|
.map((item) => item.detailCdNm)
|
||||||
|
.reverse();
|
||||||
|
|
||||||
|
setFilterMethods(detailCdNmValues);
|
||||||
|
}
|
||||||
|
}, [categoryDatas, tab]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tabType = tab === 0 ? "CAT00101" : "CAT00102";
|
||||||
|
|
||||||
|
dispatch(getSubCategory({ lgCatCd, tabType }));
|
||||||
|
}, [tab, dispatch]);
|
||||||
|
|
||||||
|
const handleItemClick = useCallback(
|
||||||
|
({ index }) => {
|
||||||
|
if (index === tab) return;
|
||||||
|
|
||||||
|
setTab(index);
|
||||||
|
},
|
||||||
|
[tab]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelectFilter = useCallback(({ selected }) => {
|
||||||
|
setDropDownTab(selected);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const filterType = dropDownTab === 0 ? "CAT00202" : "CAT00201";
|
||||||
|
|
||||||
|
dispatch(getSubCategory({ lgCatCd, filterType }));
|
||||||
|
}, [dropDownTab, dispatch]);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"panelInfos",
|
||||||
|
panelInfos.panelInfo,
|
||||||
|
"categoryDatas",
|
||||||
|
categoryDatas
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Spotlight.focus(`tab-${tab}`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TPanel>
|
||||||
|
<THeader title="Category" />
|
||||||
|
<TBody className={css.tBody}>
|
||||||
|
<div className={css.paddingContainer}>
|
||||||
|
{panelInfos && panelInfos.panelInfo && categoryDatas && (
|
||||||
|
<SectionTitle
|
||||||
|
title={lgCatNm}
|
||||||
|
className={css.title}
|
||||||
|
itemCount={
|
||||||
|
categoryItemInfos
|
||||||
|
? categoryItemInfos.length > 0
|
||||||
|
? categoryItemInfos[0].total
|
||||||
|
: "0"
|
||||||
|
: categoryShowInfos.length > 0
|
||||||
|
? categoryShowInfos[0].total
|
||||||
|
: "0"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{buttonTabList && buttonTabList.length > 0 && (
|
||||||
|
<div className={css.tabContainer}>
|
||||||
|
<TButtonTab
|
||||||
|
contents={buttonTabList}
|
||||||
|
onItemClick={handleItemClick}
|
||||||
|
selectedIndex={tab}
|
||||||
|
listType={LIST_TYPE.medium}
|
||||||
|
/>
|
||||||
|
<TDropDown
|
||||||
|
className={css.dropdown}
|
||||||
|
onSelect={handleSelectFilter}
|
||||||
|
selectedIndex={dropDownTab}
|
||||||
|
width="small"
|
||||||
|
>
|
||||||
|
{filterMethods}
|
||||||
|
</TDropDown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{tab === 0 && <ShowContents />}
|
||||||
|
{tab === 1 && <ItemContents />}
|
||||||
|
</div>
|
||||||
|
</TBody>
|
||||||
|
</TPanel>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@import "../../style/CommonStyle.module.less";
|
||||||
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
.tBody {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 90px);
|
||||||
|
background-color: @BG_COLOR_01;
|
||||||
|
|
||||||
|
.paddingContainer {
|
||||||
|
padding-left: 60px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 38px 0 33px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabContainer {
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user