[FeaturedBrandsPanel] section CATEGORY, scroll & focus 정책 적용

This commit is contained in:
younghoon100.park
2024-04-08 20:36:14 +09:00
parent d6e4d79d3c
commit ebced41f3d
10 changed files with 172 additions and 115 deletions

View File

@@ -25,11 +25,13 @@ import {
getBrandTSVInfo,
} from "../../actions/brandActions";
import { changeAppStatus, setHidePopup } from "../../actions/commonActions";
import { updatePanel } from "../../actions/panelActions";
import TBody from "../../components/TBody/TBody";
import TButton, { TYPES } from "../../components/TButton/TButton";
import TPanel from "../../components/TPanel/TPanel";
import TPopUp from "../../components/TPopUp/TPopUp";
import useScrollTo from "../../hooks/useScrollTo";
import { panel_names } from "../../utils/Config";
import { $L } from "../../utils/helperMethods";
import Banner from "./Banner/Banner";
import FeaturedBestSeller from "./FeaturedBestSeller/FeaturedBestSeller";
@@ -200,6 +202,7 @@ export default function FeaturedBrandsPanel() {
const [selectedCatCdLv1, setSelectedCatCdLv1] = useState();
const [selectedCatCdLv2, setSelectedCatCdLv2] = useState();
const [targetId, setTargetId] = useState();
const [_, setForceUpdate] = useState({});
const firstFocusableTargetId = useRef();
const getFirstFocusableTargetIdJob = useRef(
@@ -253,11 +256,23 @@ export default function FeaturedBrandsPanel() {
useEffect(() => {
if (panelInfo) {
scrollTopBody({ animate: false });
setSelectedPatnrId(panelInfo);
console.log("pyh panelInfo", panelInfo);
console.log("pyh panelInfo.length", Object.keys(panelInfo).length);
if (Object.keys(panelInfo).length > 1) {
setSelectedBrandInfo(
findItemByValue(panelInfo.brandInfo, panelInfo.patnrId)
);
setTargetId(panelInfo.targetId);
// pyh Todo, edit, scroll timing
queueMicrotask(() => scrollTopBody({ y: panelInfo.y, animate: false }));
return;
}
setSelectedPatnrId(panelInfo?.patnrId);
setTargetId("spotlightId-" + panelInfo);
}
}, [panelInfo]);
}, []);
useEffect(() => {
if (!brandInfo) {
@@ -276,38 +291,33 @@ export default function FeaturedBrandsPanel() {
}, [brandInfo, targetId]);
useEffect(() => {
return () => {
getFirstFocusableTargetIdJob.current.stop();
focusJob.current.stop();
};
}, []);
if (brandInfo) {
if (selectedPatnrId) {
setSelectedBrandInfo(findItemByValue(brandInfo, selectedPatnrId));
setSelectedCatCd();
setSelectedHstNm();
setSelectedSeriesId();
setSelectedCatCdLv1();
setSelectedCatCdLv2();
useEffect(() => {
if (brandInfo && selectedPatnrId) {
setSelectedBrandInfo(findItemByValue(brandInfo, selectedPatnrId));
setSelectedCatCd();
setSelectedHstNm();
setSelectedSeriesId();
setSelectedCatCdLv1();
setSelectedCatCdLv2();
dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandLiveChannelInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandTSVInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandBestSeller({ patnrId: selectedPatnrId }));
dispatch(getBrandRecommendedShowInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandCreatorsInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandSeriesInfo({ patnrId: selectedPatnrId }));
dispatch(
getBrandCategoryInfo({
patnrId: selectedPatnrId,
})
);
dispatch(
getBrandShowroom({
patnrId: selectedPatnrId,
})
);
dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandLiveChannelInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandTSVInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandBestSeller({ patnrId: selectedPatnrId }));
dispatch(getBrandRecommendedShowInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandCreatorsInfo({ patnrId: selectedPatnrId }));
dispatch(getBrandSeriesInfo({ patnrId: selectedPatnrId }));
dispatch(
getBrandCategoryInfo({
patnrId: selectedPatnrId,
})
);
dispatch(
getBrandShowroom({
patnrId: selectedPatnrId,
})
);
}
}
}, [brandInfo, dispatch, selectedPatnrId]);
@@ -344,17 +354,39 @@ export default function FeaturedBrandsPanel() {
}
}, [selectedSeriesId, dispatch]);
// pyh Todo, scroll issue
useEffect(() => {
if (selectedCatCdLv1 === "") {
dispatch(
getBrandLayoutInfo({
patnrId: selectedPatnrId,
catCdLv1: selectedCatCdLv1,
})
);
}
}, [dispatch, selectedCatCdLv1]);
setForceUpdate({});
}, [selectedCatCdLv1, selectedCatCdLv2]);
useEffect(() => {
return () => {
if (brandInfo) {
console.log("pyh -------------------- unmounted --------------------");
const marker = document.querySelector(`[data-marker="scroll-marker"]`);
const lastFocusedTarget = Spotlight.getCurrent();
if (marker && lastFocusedTarget) {
const targetId = lastFocusedTarget.getAttribute("data-spotlight-id");
const markerRect = marker.getBoundingClientRect();
const lastFocusedTargetRect =
lastFocusedTarget.getBoundingClientRect();
const y = lastFocusedTargetRect.top - markerRect.top;
dispatch(
updatePanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { ...panelInfo, brandInfo, targetId, y },
})
);
}
getFirstFocusableTargetIdJob.current.stop();
focusJob.current.stop();
}
};
}, [brandInfo]);
const handleTopButtonClick = useCallback(() => {
if (!firstFocusableTargetId.current) {
@@ -381,7 +413,6 @@ export default function FeaturedBrandsPanel() {
}, [dispatch]);
return (
/* scenario page 98 */
<TPanel className={css.tPanel}>
<TBody className={css.tBody} cbScrollTo={getScrollToBody}>
<div data-marker="scroll-marker" />

View File

@@ -101,6 +101,7 @@ export default memo(function FeaturedCategory({
brandCategoryProductDetailInfoItem={
brandCategoryProductDetailInfoItem
}
index={index}
isCarousel={array.length > 1}
key={"brand-category-product-info-" + index}
scrollTopBody={scrollTopBody}

View File

@@ -13,6 +13,7 @@ const Container = SpotlightContainerDecorator(
export default function FeaturedCategoryContents({
brandCategoryProductDetailInfoItem,
index,
isCarousel,
scrollTopBody,
selectedPatnrId,
@@ -22,11 +23,12 @@ export default function FeaturedCategoryContents({
return (
<Container className={css.container}>
<h3 data-subtitle={catNm}>{catNm}</h3>
<h3 data-category-subtitle-index={index}>{catNm}</h3>
{isCarousel ? (
<FeaturedCategoryProductList
brandCategoryProductDetailInfo={brandCategoryProductDetailInfo}
catNm={catNm}
index={index}
patnrId={patnrId}
scrollTopBody={scrollTopBody}
selectedPatnrId={selectedPatnrId}

View File

@@ -3,7 +3,7 @@
.container {
> h3 {
margin: 36px 0 12px;
margin-top: 14px;
padding-left: 60px;
font-weight: bold;
font-size: 36px;

View File

@@ -2,11 +2,10 @@
@import "../../../../../style/utils.module.less";
.container {
padding-left: 60px;
ul {
.flex(@justifyCenter: flex-start);
flex-wrap: wrap;
padding: 22px 0 0 60px;
li {
margin: 0 18px 15px 0;

View File

@@ -9,7 +9,7 @@ import {
import { pushPanel } from "../../../../../actions/panelActions";
import TItemCard from "../../../../../components/TItemCard/TItemCard";
import TVirtualGridList from "../../../../../components/TVirtualGridList/TVirtualGridList";
import TScroller from "../../../../../components/TScroller/TScroller";
import useScrollTo from "../../../../../hooks/useScrollTo";
import { panel_names } from "../../../../../utils/Config";
import css from "./FeaturedCategoryProductList.module.less";
@@ -17,6 +17,7 @@ import css from "./FeaturedCategoryProductList.module.less";
export default memo(function FeaturedCategoryProductList({
brandCategoryProductDetailInfo,
catNm,
index,
patnrId,
scrollTopBody,
selectedPatnrId,
@@ -43,81 +44,79 @@ export default memo(function FeaturedCategoryProductList({
const setScrollTopBody = useCallback(() => {
const marker = document.querySelector(`[data-marker="scroll-marker"]`);
const sectionTitle = document.querySelector(`[data-subtitle="${catNm}"]`);
const subTitle = document.querySelector(
`[data-category-subtitle-index="${index}"]`
);
if (marker && sectionTitle) {
if (marker && subTitle) {
const markerRect = marker.getBoundingClientRect();
const sectionTitleRect = sectionTitle.getBoundingClientRect();
const subTitleRect = subTitle.getBoundingClientRect();
const subSectionGap = 36;
const y = sectionTitleRect.top - markerRect.top;
const y = subTitleRect.top - markerRect.top - subSectionGap;
timeoutRef.current = setTimeout(() => scrollTopBody({ y }));
} else {
console.error("subTitle not found");
}
}, []);
const renderItem = useCallback(
({ index, ...rest }) => {
const { imgUrl, offerInfo, prdtId, prdtNm, priceInfo } =
brandCategoryProductDetailInfo[index];
const handleBlur = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}, []);
const handleBlur = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
const handleClick = () => {
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
};
const handleFocus = () => {
if (cursorVisible) {
return;
}
setScrollTopBody();
};
return (
<TItemCard
imageAlt={prdtNm}
imageSource={imgUrl}
onBlur={handleBlur}
onClick={handleClick}
onFocus={handleFocus}
offerInfo={offerInfo}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
{...rest}
/>
const handleClick = useCallback(
(prdtId) => {
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},
[brandCategoryProductDetailInfo, cursorVisible, dispatch, patnrId]
[dispatch, patnrId]
);
const handleFocus = useCallback(() => {
if (cursorVisible) {
return;
}
setScrollTopBody();
}, [cursorVisible]);
return (
<div
className={css.container}
id={"featured-category-product-list-id-" + catNm}
>
{brandCategoryProductDetailInfo && (
<TVirtualGridList
cbScrollTo={getScrollTo}
className={css.tVirtualGridList}
dataSize={brandCategoryProductDetailInfo.length}
direction="horizontal"
itemHeight={438}
itemWidth={324}
spacing={18}
renderItem={renderItem}
/>
)}
<TScroller
cbScrollTo={getScrollTo}
direction="horizontal"
noScrollByWheel
>
<ul>
{brandCategoryProductDetailInfo.map(
({ imgUrl, offerInfo, prdtId, prdtNm, priceInfo }, itemIndex) => (
<li key={"brand-category-product-detail-info-" + prdtId}>
<TItemCard
imageAlt={prdtNm}
imageSource={imgUrl}
onBlur={handleBlur}
onClick={() => handleClick(prdtId)}
onFocus={handleFocus}
offerInfo={offerInfo}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
/>
</li>
)
)}
</ul>
</TScroller>
</div>
);
});

View File

@@ -3,14 +3,25 @@
.container {
position: relative;
.size(@w: 100%, @h: 438px);
.size(@w: 100%, @h: 482px);
// tVirtualGridListContainer
// tScroller
> div:nth-child(1) {
.size(@w: inherit, @h: inherit);
.size(@w: 100%, @h: 482px);
&.tVirtualGridList {
padding-left: 60px;
ul {
display: flex;
align-items: center;
padding: 22px 0 22px 60px;
> li {
flex: none;
// tItemCard
> div {
margin-right: 18px;
}
}
}
}
}

View File

@@ -62,8 +62,9 @@ export default function FeaturedCategoryNav({
if (marker && sectionTitle) {
const markerRect = marker.getBoundingClientRect();
const sectionTitleRect = sectionTitle.getBoundingClientRect();
const sectionGap = 58;
const y = sectionTitleRect.top - markerRect.top;
const y = sectionTitleRect.top - markerRect.top - sectionGap;
timeoutRef.current = setTimeout(() => scrollTopBody({ y }));
}

View File

@@ -4,7 +4,7 @@
.nav {
position: relative;
.size(@w: 100%, @h: 132px);
margin-top: -48px;
margin: -48px 0 36px 0;
background-color: #e9e9e9;
// tScroller

View File

@@ -1,10 +1,14 @@
import React, { memo, useCallback } from "react";
import { useDispatch } from "react-redux";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { updatePanel } from "../../../actions/panelActions";
import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo";
import { panel_names } from "../../../utils/Config";
import QuickMenuItem from "../QuickMenu/QuickMenuItem/QuickMenuItem";
import css from "./QuickMenu.module.less";
@@ -24,6 +28,8 @@ export default memo(function QuickMenu({
true
);
const dispatch = useDispatch();
const handleBlur = useCallback((index) => {
if (index !== 0) {
return;
@@ -38,9 +44,16 @@ export default memo(function QuickMenu({
return;
}
dispatch(
updatePanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: selectedPatnrId,
})
);
setSelectedPatnrId(patnrId);
},
[selectedPatnrId]
[dispatch, selectedPatnrId]
);
const handleFocus = useCallback((index) => {