[CategoryPanel] 카테고리 패널 작업 진행 중 / TODO: ITEM 탭 구현 및 데이터 백그라운드 페칭

This commit is contained in:
hyunwoo93.cha
2024-02-13 22:03:40 +09:00
parent 675c948ae3
commit cbbe434954
14 changed files with 518 additions and 1 deletions

View File

@@ -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>
);
}

View File

@@ -0,0 +1,5 @@
import React from "react";
export default function ItemContents() {
return <p>ITEM</p>;
}

View File

@@ -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>
</>
)}
</>
);
}

View File

@@ -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);
}
}
}

View File

@@ -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>
);
}

View File

@@ -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;
}
}
}

View File

@@ -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>
);
}

View File

@@ -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);
}
}
}

View File

@@ -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>
);
}

View File

@@ -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;
}
}

View File

@@ -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>
);
} }

View File

@@ -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%);
}
}
}
}