vertical pagenator

This commit is contained in:
yonghyon
2024-05-31 15:58:08 +09:00
parent 80f1967f6f
commit dd705d0071
17 changed files with 428 additions and 304 deletions

View File

@@ -0,0 +1,3 @@
ES5='true'
DISABLE_NEW_JSX_TRANSFORM='true'
REACT_APP_MODE=DEBUG

View File

@@ -201,11 +201,14 @@ function AppBase(props) {
useEffect(() => { useEffect(() => {
const keyDownEvent = (event) => { const keyDownEvent = (event) => {
dispatch(changeAppStatus({ cursorVisible: false })); dispatch(changeAppStatus({ cursorVisible: false }));
Spotlight.setPointerMode(false);
}; };
const mouseMoveEvent = (event) => { const mouseMoveEvent = (event) => {
dispatch(changeAppStatus({ cursorVisible: true })); dispatch(changeAppStatus({ cursorVisible: true }));
Spotlight.setPointerMode(true);
hideCursor.current.start(() => { hideCursor.current.start(() => {
dispatch(changeAppStatus({ cursorVisible: false })); dispatch(changeAppStatus({ cursorVisible: false }));
Spotlight.setPointerMode(false);
}); });
}; };
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem) {
@@ -222,9 +225,9 @@ function AppBase(props) {
document.addEventListener("webOSRelaunch", handleRelaunchEvent); document.addEventListener("webOSRelaunch", handleRelaunchEvent);
//local virtual cursorvisiblilty //local virtual cursorvisiblilty
if (typeof window === "object" && !window.PalmSystem) { if (typeof window === "object" && !window.PalmSystem) {
document.addEventListener("keydown", keyDownEvent); document.addEventListener("keydown", keyDownEvent, true);
document.addEventListener("mousemove", mouseMoveEvent); document.addEventListener("mousemove", mouseMoveEvent, true);
document.addEventListener("wheel", mouseMoveEvent); document.addEventListener("wheel", mouseMoveEvent, true);
} }
return () => { return () => {
document.removeEventListener("visibilitychange", visibilityChanged); document.removeEventListener("visibilitychange", visibilityChanged);

View File

@@ -102,7 +102,7 @@ export const getHttpHeaderForServiceRequest =
}; };
convertedRes["X-Device-Personalization"] = "Y"; convertedRes["X-Device-Personalization"] = "Y";
if (window.PalmSystem && window.PalmSystem.identifier) { if (window.PalmSystem && window.PalmSystem.identifier && process.env.REACT_APP_MODE !== "DEBUG") {
convertedRes["app_id"] = window.PalmSystem.identifier ?? appinfo.id; convertedRes["app_id"] = window.PalmSystem.identifier ?? appinfo.id;
} else { } else {
convertedRes["app_id"] = appinfo.id; convertedRes["app_id"] = appinfo.id;

View File

@@ -0,0 +1,170 @@
/**
* TVerticalPagenator
*
* @module TVerticalPagenator
*/
import classNames from 'classnames';
import React, {useState, useCallback, useEffect, useRef} from 'react';
import css from './TVerticalPagenator.module.less';
import TScroller from '../TScroller/TScroller';
import { useSelector } from 'react-redux';
import * as HelperMethods from "../../utils/helperMethods";
import usePrevious from '../../hooks/usePrevious';
import Spotlight from '@enact/spotlight';
const TVerticalPagenator = ({
className,
spotlightId,
defaultPageIndex=0,
children,
disabled,
pageSpotIds = [],
onPageChanged,
visible = "hidden",
scrollPositionRef,
cbChangePageRef,
scrollTop=false,
topMargin=0,
...rest
}) => {
const [pageIndex, setPageIndex] = useState(Number(defaultPageIndex) !== NaN ? Number(defaultPageIndex): 0);
const pagenatorRef = useRef(null);
const pageIndexRef = usePrevious(pageIndex);
const isScrolling = useRef(false);
const scrollTopPositionRef = useRef(0);
const scrollTo = useRef(null);
const cursorVisible = useSelector((state) => state.common.appStatus.cursorVisible);
const cursorVisibleRef = usePrevious(cursorVisible);
useEffect(() => {
if(spotlightId){
pagenatorRef.current = document.querySelector(`[data-spotlight-id="${spotlightId}"]`);
}
}, []);
const onFocusItem = useCallback((ev)=>{
if(!pagenatorRef.current){
return;
}
ev.stopPropagation();
ev.preventDefault();
if (cursorVisibleRef.current) {
return;
}
pageSpotIds.forEach((element, index) => {
const pageNode = document.querySelector(`[data-spotlight-id="${element}"]`);
if(pageNode.contains(ev.target))
setPageIndex(index);
}
);
},[pageSpotIds]);
const handleWheel = useCallback((ev) => {
if(pagenatorRef.current.contains(ev.target)){
ev.stopPropagation();
if(disabled){
return;
}
let newIndex = ev.deltaY > 0 ? pageIndexRef.current+1: pageIndexRef.current -1;
newIndex = Math.max(newIndex, 0);
newIndex = Math.min(newIndex, pageSpotIds.length-1);
setPageIndex(newIndex);
}
}, [disabled, pageSpotIds]);
const getScrollTo = useCallback((cbScrollTo) => {
scrollTo.current = cbScrollTo;
},[]);
useEffect(() => {
if (scrollTop && scrollTo.current) {
scrollTo.current({ position: { y: 0, x: 0 }, animate: true });
setPageIndex(0);
}
}, [scrollTop]);
useEffect(() => {
document.addEventListener("wheel", handleWheel, true);
return () => {
document.removeEventListener('wheel', handleWheel);
};
}, [handleWheel]);
useEffect(() => {
return () => {
scrollTo.current = null;
};
}, []);
const moveToPage = useCallback((index=0, animate=true)=>{
if(pageSpotIds[index]){
const currentNode = document.querySelector(
`[data-spotlight-id="${pageSpotIds[index]}"]`
);
if(currentNode){
const distance = Math.round(currentNode.offsetTop - HelperMethods.scaleH(topMargin));
scrollTo.current && scrollTo.current({ position:{x:0,y: distance >=0 ? distance:0}, animate: animate, focus: true });
Spotlight.focus(currentNode);
}
}
if(onPageChanged){
onPageChanged(index);
}
},[topMargin, pageSpotIds, onPageChanged]);
useEffect(() => {
moveToPage(pageIndex, true);
}, [pageIndex]);
useEffect(() => {
if(cbChangePageRef){
cbChangePageRef.current = (index)=>{
moveToPage(index, false);
setPageIndex(index);
};
}
}, [cbChangePageRef, moveToPage]);
const _onScrollStart = useCallback(() => {
isScrolling.current = true;
}, []);
const _onScrollStop = useCallback(() => {
isScrolling.current = false;
}, []);
const _onScroll = useCallback((ev) => {
isScrolling.current = true;
scrollTopPositionRef.current = ev.scrollTop;
if(scrollPositionRef){
scrollPositionRef.current = ev.scrollTop;
}
}, [scrollPositionRef]);
if(!spotlightId){
console.warn('TVerticalPagenator has no spotlightId');
return null;
}
return (
<TScroller
{...rest}
id={spotlightId}
spotlightId={spotlightId}
className={classNames(css.verticalPagenator, className)}
focusableScrollbar={visible !== "hidden"}
noScrollByWheel={true}
noScrollByDrag={true}
verticalScrollbar={visible}
onFocus={onFocusItem}
cbScrollTo={getScrollTo}
onScroll={_onScroll}
onScrollStart={_onScrollStart}
onScrollStop={_onScrollStop}
>
{children}
</TScroller>
);
};
export default TVerticalPagenator;

View File

@@ -0,0 +1,8 @@
@import "../../style/CommonStyle.module.less";
@import "../../style/utils.module.less";
.verticalPagenator {
// height: ~"calc(100% - @{fitTv-tHeader-height})";
width: 100%;
position: relative;
}

View File

@@ -0,0 +1,3 @@
{
"main": "TVerticalPagenator.jsx"
}

View File

@@ -6,21 +6,16 @@ export const getSystemInfo = (
parameters, parameters,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") { return new LS2Request().send({
console.log("LUNA SEND getSystemInfo"); service: "luna://com.webos.service.tv.systemproperty",
return "Some Hard Coded Mock Data"; method: "getSystemInfo",
} else { subscribe: false,
return new LS2Request().send({ parameters: parameters,
service: "luna://com.webos.service.tv.systemproperty", onSuccess,
method: "getSystemInfo", onFailure,
subscribe: false, onComplete,
parameters: parameters, });
onSuccess,
onFailure,
onComplete,
});
}
} else { } else {
onSuccess({ returnValue: true, sdkVersion: "local" }); onSuccess({ returnValue: true, sdkVersion: "local" });
onComplete(); onComplete();
@@ -30,21 +25,16 @@ export const getDeviceId = (
parameters, parameters,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") { return new LS2Request().send({
console.log("LUNA SEND getDeviceId"); service: "luna://com.webos.service.sm",
return "Some Hard Coded Mock Data"; method: "deviceid/getIDs",
} else { subscribe: false,
return new LS2Request().send({ parameters: parameters,
service: "luna://com.webos.service.sm", onSuccess,
method: "deviceid/getIDs", onFailure,
subscribe: false, onComplete,
parameters: parameters, });
onSuccess,
onFailure,
onComplete,
});
}
} else { } else {
onSuccess({ onSuccess({
returnValue: true, returnValue: true,
@@ -57,11 +47,7 @@ export const getLoginUserData = (
parameters, parameters,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") {
console.log("getLoginUserData");
return "Mock Data";
} else {
return new LS2Request().send({ return new LS2Request().send({
service: "luna://com.webos.service.accountmanager", service: "luna://com.webos.service.accountmanager",
method: "getLoginID", method: "getLoginID",
@@ -71,7 +57,6 @@ export const getLoginUserData = (
onFailure, onFailure,
onComplete, onComplete,
}); });
}
} else { } else {
onSuccess({ onSuccess({
subscribed: false, subscribed: false,
@@ -119,11 +104,7 @@ export const launchMembershipAppNew = (
webOSVersion, webOSVersion,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") {
console.log("LUNA SEND launchMembershipAppNew");
return "Some Hard Coded Mock Data";
} else {
return new LS2Request().send({ return new LS2Request().send({
service: "luna://com.webos.applicationManager", service: "luna://com.webos.applicationManager",
method: "launchDefaultApp", method: "launchDefaultApp",
@@ -139,7 +120,6 @@ export const launchMembershipAppNew = (
onFailure, onFailure,
onComplete, onComplete,
}); });
}
} }
}; };
@@ -147,11 +127,7 @@ export const launchMembershipApp = (
webOSVersion, webOSVersion,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") {
console.log("LUNA SEND launchMembershipApp");
return "Some Hard Coded Mock Data";
} else {
return new LS2Request().send({ return new LS2Request().send({
service: "luna://com.webos.applicationManager", service: "luna://com.webos.applicationManager",
method: "launch", method: "launch",
@@ -167,6 +143,5 @@ export const launchMembershipApp = (
onFailure, onFailure,
onComplete, onComplete,
}); });
}
} }
}; };

View File

@@ -45,11 +45,7 @@ export const getHttpHeaderForServiceRequest = ({
onFailure, onFailure,
onComplete, onComplete,
}) => { }) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") {
console.log("LUNA SEND getHttpHeaderForServiceRequest");
return "Some Hard Coded Mock Data";
} else {
if (httpHeaderHandler) { if (httpHeaderHandler) {
httpHeaderHandler.cancel(); httpHeaderHandler.cancel();
} }
@@ -63,7 +59,6 @@ export const getHttpHeaderForServiceRequest = ({
onComplete, onComplete,
}); });
return httpHeaderHandler; return httpHeaderHandler;
}
} else { } else {
onSuccess({ onSuccess({
HOST: "US.nextlgsdp.com", HOST: "US.nextlgsdp.com",
@@ -92,11 +87,7 @@ export const getSystemSettings = (
parameters, parameters,
{ onSuccess, onFailure, onComplete } { onSuccess, onFailure, onComplete }
) => { ) => {
if (typeof window === "object" && window.PalmSystem) { if (typeof window === "object" && window.PalmSystem && process.env.REACT_APP_MODE !== "DEBUG") {
if (process.env.REACT_APP_MODE === "DEBUG") {
console.log("LUNA SEND getSystemSettings");
return "Some Hard Coded Mock Data";
} else {
return new LS2Request().send({ return new LS2Request().send({
service: "luna://com.webos.settingsservice", service: "luna://com.webos.settingsservice",
method: "getSystemSettings", method: "getSystemSettings",
@@ -106,7 +97,6 @@ export const getSystemSettings = (
onFailure, onFailure,
onComplete, onComplete,
}); });
}
} else if (typeof window === "object") { } else if (typeof window === "object") {
const language = const language =
typeof window.navigator === "object" typeof window.navigator === "object"

View File

@@ -6,7 +6,7 @@ import stringReSourceRu from "../../resources/ru/strings.json";
import { Job } from "@enact/core/util"; import { Job } from "@enact/core/util";
let _boundingRectCache = {}; let _boundingRectCache = {};
const BOUNDING_RECT_IGNORE_TIME = 100; const BOUNDING_RECT_IGNORE_TIME = 10;
const generateUUID = () => { const generateUUID = () => {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -320,3 +320,15 @@ export const isChildFullyShowing = (parentNode, childNode) => {
return false; return false;
}; };
export const getRectDiff = (element1, element2) => {
const element1Rect = getBoundingClientRect(element1);
const element2Rect = getBoundingClientRect(element2);
return ({
right: element1Rect.right - element2Rect.right,
left: element1Rect.left - element2Rect.left,
top: element1Rect.top - element2Rect.top,
bottom: element1Rect.bottom - element2Rect.bottom,
});
};

View File

@@ -12,8 +12,7 @@ import TItemCard from "../../../components/TItemCard/TItemCard";
import TScroller from "../../../components/TScroller/TScroller"; import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset"; import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo"; import useScrollTo from "../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../hooks/useScrollTopByDistance"; import { panel_names } from "../../../utils/Config";
import { LOG_MENU, panel_names } from "../../../utils/Config";
import { $L, scaleW } from "../../../utils/helperMethods"; import { $L, scaleW } from "../../../utils/helperMethods";
import css from "./BestSeller.module.less"; import css from "./BestSeller.module.less";
@@ -23,13 +22,12 @@ const Container = SpotlightContainerDecorator(
"div" "div"
); );
const BestSeller = ({ order, scrollTopBody, handleItemFocus }) => { const BestSeller = ({ order, scrollTopBody, spotlightId, handleItemFocus }) => {
const { getScrollTo, scrollLeft } = useScrollTo(); const { getScrollTo, scrollLeft } = useScrollTo();
const { handleScrollReset, handleStopScrolling } = useScrollReset( const { handleScrollReset, handleStopScrolling } = useScrollReset(
scrollLeft, scrollLeft,
true true
); );
const { scrollTopByDistance } = useScrollTopByDistance();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -90,20 +88,12 @@ const BestSeller = ({ order, scrollTopBody, handleItemFocus }) => {
if (cursorVisible) { if (cursorVisible) {
return; return;
} }
scrollTopByDistance(
`[data-marker="scroll-marker"]`,
`[data-title-index="homeBestSellerTitle"]`,
scrollTopBody,
36
);
}, },
[ [
cursorVisible, cursorVisible,
_handleItemFocus, _handleItemFocus,
handleScrollReset, handleScrollReset,
scrollTopBody, scrollTopBody
scrollTopByDistance,
] ]
); );
@@ -118,12 +108,12 @@ const BestSeller = ({ order, scrollTopBody, handleItemFocus }) => {
const _handleItemFocus = useCallback(() => { const _handleItemFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(LOG_MENU.HOME_BEST_SELLER); handleItemFocus();
} }
}, [handleItemFocus]); }, [handleItemFocus]);
return ( return (
<Container className={css.bestSellerWrap} style={orderStyle}> <Container className={css.bestSellerWrap} style={orderStyle} spotlightId={spotlightId}>
<SectionTitle <SectionTitle
title={$L("BEST SELLER")} title={$L("BEST SELLER")}
data-title-index="homeBestSellerTitle" data-title-index="homeBestSellerTitle"

View File

@@ -10,7 +10,6 @@ import { changeAppStatus } from "../../../actions/commonActions";
import { getHomeMainContents } from "../../../actions/homeActions"; import { getHomeMainContents } from "../../../actions/homeActions";
import CustomImage from "../../../components/CustomImage/CustomImage"; import CustomImage from "../../../components/CustomImage/CustomImage";
import useScrollReset from "../../../hooks/useScrollReset"; import useScrollReset from "../../../hooks/useScrollReset";
import { LOG_MENU } from "../../../utils/Config";
import css from "./HomeBanner.module.less"; import css from "./HomeBanner.module.less";
import Random from "./RandomUnit"; import Random from "./RandomUnit";
import Rolling from "./RollingUnit"; import Rolling from "./RollingUnit";
@@ -32,6 +31,7 @@ export default function HomeBanner({
selectTemplate, selectTemplate,
order, order,
firstSpot, firstSpot,
spotlightId,
handleItemFocus, handleItemFocus,
}) { }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -188,12 +188,12 @@ export default function HomeBanner({
const _handleItemFocus = useCallback(() => { const _handleItemFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(LOG_MENU.HOME_TOP); handleItemFocus();
} }
}, [handleItemFocus]); }, [handleItemFocus]);
return ( return (
<Container className={css.container} spotlightId={"top"} style={orderStyle}> <Container className={css.container} spotlightId={spotlightId} style={orderStyle}>
<div className={css.homeTemplateBox}> <div className={css.homeTemplateBox}>
<ContainerBasic <ContainerBasic
className={css.smallBox} className={css.smallBox}

View File

@@ -8,8 +8,6 @@ import SectionTitle from "../../../components/SectionTitle/SectionTitle";
import TScroller from "../../../components/TScroller/TScroller"; import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset"; import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo"; import useScrollTo from "../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../hooks/useScrollTopByDistance";
import { LOG_MENU } from "../../../utils/Config";
import { $L, scaleW } from "../../../utils/helperMethods"; import { $L, scaleW } from "../../../utils/helperMethods";
import css from "./HomeOnSale.module.less"; import css from "./HomeOnSale.module.less";
import HomeOnSaleItem from "./HomeOnSaleItem/HomeOnSaleItem"; import HomeOnSaleItem from "./HomeOnSaleItem/HomeOnSaleItem";
@@ -19,8 +17,8 @@ const Container = SpotlightContainerDecorator(
"div" "div"
); );
const HomeOnSale = ({ order, scrollTopBody, handleItemFocus, ...rest }) => { const HomeOnSale = ({ order, scrollTopBody, handleItemFocus,spotlightId, ...rest }) => {
const { scrollTopByDistance } = useScrollTopByDistance();
const { getScrollTo, scrollLeft } = useScrollTo(); const { getScrollTo, scrollLeft } = useScrollTo();
const { handleScrollReset, handleStopScrolling } = useScrollReset( const { handleScrollReset, handleStopScrolling } = useScrollReset(
scrollLeft, scrollLeft,
@@ -59,13 +57,6 @@ const HomeOnSale = ({ order, scrollTopBody, handleItemFocus, ...rest }) => {
if (cursorVisible) { if (cursorVisible) {
return; return;
} }
scrollTopByDistance(
`[data-marker="scroll-marker"]`,
`[data-title-index="homeOnSale"]`,
scrollTopBody,
36
);
}, },
[ [
cursorVisible, cursorVisible,
@@ -73,8 +64,7 @@ const HomeOnSale = ({ order, scrollTopBody, handleItemFocus, ...rest }) => {
handleScrollReset, handleScrollReset,
handleScrollRight, handleScrollRight,
homeOnSaleInfos?.length, homeOnSaleInfos?.length,
scrollTopBody, scrollTopBody
scrollTopByDistance,
] ]
); );
@@ -89,12 +79,12 @@ const HomeOnSale = ({ order, scrollTopBody, handleItemFocus, ...rest }) => {
const _handleItemFocus = useCallback(() => { const _handleItemFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(LOG_MENU.HOME_ON_SALE); handleItemFocus();
} }
}, [handleItemFocus]); }, [handleItemFocus]);
return ( return (
<Container {...rest} className={css.container} style={orderStyle}> <Container {...rest} className={css.container} style={orderStyle} spotlightId={spotlightId}>
<div className={css.onSaleWrap}> <div className={css.onSaleWrap}>
<SectionTitle <SectionTitle
className={css.subTitle} className={css.subTitle}

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from "react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
@@ -26,8 +26,7 @@ import TPanel from "../../components/TPanel/TPanel";
import TPopUp from "../../components/TPopUp/TPopUp"; import TPopUp from "../../components/TPopUp/TPopUp";
import useDebugKey from "../../hooks/useDebugKey"; import useDebugKey from "../../hooks/useDebugKey";
import useLogService from "../../hooks/useLogService"; import useLogService from "../../hooks/useLogService";
import useScrollTo from "../../hooks/useScrollTo"; import { LOG_MENU, ACTIVE_POPUP, panel_names } from "../../utils/Config";
import { ACTIVE_POPUP, panel_names } from "../../utils/Config";
import { $L } from "../../utils/helperMethods"; import { $L } from "../../utils/helperMethods";
import { SpotlightIds } from "../../utils/SpotlightIds"; import { SpotlightIds } from "../../utils/SpotlightIds";
import BestSeller from "../HomePanel/BestSeller/BestSeller"; import BestSeller from "../HomePanel/BestSeller/BestSeller";
@@ -37,6 +36,8 @@ import css from "../HomePanel/HomePanel.module.less";
import PopularShow from "../HomePanel/PopularShow/PopularShow"; import PopularShow from "../HomePanel/PopularShow/PopularShow";
import SubCategory from "../HomePanel/SubCategory/SubCategory"; import SubCategory from "../HomePanel/SubCategory/SubCategory";
import EventPopUpBanner from "./EventPopUpBanner/EventPopUpBanner"; import EventPopUpBanner from "./EventPopUpBanner/EventPopUpBanner";
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
import usePrevious from "../../hooks/usePrevious";
const TEMPLATE_CODE_CONF = { const TEMPLATE_CODE_CONF = {
TOP: "DSP00101", TOP: "DSP00101",
@@ -46,26 +47,18 @@ const TEMPLATE_CODE_CONF = {
BEST_SELLER: "DSP00105", BEST_SELLER: "DSP00105",
}; };
const getOrderByValue = (array, value) =>
array.find((obj) => obj["shptmApphmDspyOptCd"] === value)?.expsOrd;
const hasTemplateCodeWithValue = (array, value) =>
Array.isArray(array) &&
array.some(
(obj) =>
Object.prototype.hasOwnProperty.call(obj, "shptmApphmDspyOptCd") &&
obj["shptmApphmDspyOptCd"] === value
);
export default function HomePanel({ isOnTop }) { export default function HomePanel({ isOnTop }) {
const { sendLogGNB } = useLogService(); const { sendLogGNB } = useLogService();
const dispatch = useDispatch(); const dispatch = useDispatch();
const [pageIndex, setPageIndex] = useState(0);
const [selectTemplate, setSelectTemplate] = useState(null); const pageIndexRef = usePrevious(pageIndex);
const [homeLayoutInfoDetail, setHomeLayoutInfoDetail] = useState([]); const spotYoffsetRef = useRef(0);
const cbChangePageRef = useRef(null);
const [btnActive, setBtnActive] = useState(true); const [btnActive, setBtnActive] = useState(true);
const [arrowBottom, setArrowBottom] = useState(true); const [arrowBottom, setArrowBottom] = useState(true);
useDebugKey({ isLandingPage: true });
const [firstSpot, setFirstSpot] = useState(false); const [firstSpot, setFirstSpot] = useState(false);
const [firstLgCatCd, setFirstLgCatCd] = useState( const [firstLgCatCd, setFirstLgCatCd] = useState(
@@ -102,17 +95,28 @@ export default function HomePanel({ isOnTop }) {
(state) => state.common.appStatus.webOSVersion (state) => state.common.appStatus.webOSVersion
); );
const arrowRef = useRef(); const selectTemplate = useMemo(()=>{
const scrollTimerRef = useRef(); if (homeTopDisplayInfos) {
const topRef = useRef(); return homeTopDisplayInfos[0].shptmTmplCd;
const spotYoffsetRef = useRef(null); }
return null;
},[homeTopDisplayInfos]);
const { const sortedHomeLayoutInfo = useMemo(()=>{
getScrollTo: getScrollToBody, if(selectTemplate && homeLayoutInfo && homeLayoutInfo.homeLayoutInfo){
scrollTop: scrollTopBody, const sorted = [...homeLayoutInfo.homeLayoutInfo].sort((x, y) => x.expsOrd - y.expsOrd);
scrollToRef, return sorted;
} = useScrollTo(); }
useDebugKey({ isLandingPage: true }); return [];
},[homeLayoutInfo, selectTemplate]);
const pageSpotIds = useMemo(()=>{
const spots = [];
for(let i=0;i<sortedHomeLayoutInfo.length;i++){
spots.push(sortedHomeLayoutInfo[i].shptmApphmDspyOptCd);
}
return spots;
},[sortedHomeLayoutInfo]);
const onCancel = useCallback(() => { const onCancel = useCallback(() => {
if (!isOnTop) { if (!isOnTop) {
@@ -125,13 +129,6 @@ export default function HomePanel({ isOnTop }) {
dispatch(setShowPopup(ACTIVE_POPUP.exitPopup)); dispatch(setShowPopup(ACTIVE_POPUP.exitPopup));
}, [isOnTop, isGnbOpened, dispatch]); }, [isOnTop, isGnbOpened, dispatch]);
const handleItemFocus = useCallback(
(nowMenu) => {
sendLogGNB(nowMenu);
},
[sendLogGNB]
);
const onExit = useCallback(() => { const onExit = useCallback(() => {
dispatch(setExitApp()); dispatch(setExitApp());
}, [dispatch]); }, [dispatch]);
@@ -140,33 +137,6 @@ export default function HomePanel({ isOnTop }) {
dispatch(setHidePopup()); dispatch(setHidePopup());
}, [dispatch]); }, [dispatch]);
const _onScroll = useCallback(() => {
if (scrollTimerRef.current) {
clearTimeout(scrollTimerRef.current);
}
scrollTimerRef.current = setTimeout(() => {
const topElement = topRef.current;
if (!topElement) return;
const topRect = topElement.getBoundingClientRect();
const windowHeight = window.innerHeight;
const topRectRelativeToViewport =
topRect.top < windowHeight ? topRect.top : 0;
const threshold = windowHeight * 0.1; // 화면 높이의 10%
if (topRectRelativeToViewport > threshold) {
setArrowBottom(false);
} else {
setArrowBottom(true);
}
}, 0);
}, []);
const _onScrollStop = (e) => {
spotYoffsetRef.current = e.scrollTop;
};
const handleTopButtonClick = () => { const handleTopButtonClick = () => {
let spotId = ""; let spotId = "";
@@ -174,7 +144,9 @@ export default function HomePanel({ isOnTop }) {
if (Spotlight.focus("banner01")) { if (Spotlight.focus("banner01")) {
spotId = "banner01"; spotId = "banner01";
} }
scrollTopBody(); if(cbChangePageRef.current){
cbChangePageRef.current(0);
}
return Spotlight.focus(spotId); return Spotlight.focus(spotId);
} }
@@ -182,7 +154,9 @@ export default function HomePanel({ isOnTop }) {
if (Spotlight.focus("banner03")) { if (Spotlight.focus("banner03")) {
spotId = "banner03"; spotId = "banner03";
} }
scrollTopBody(); if(cbChangePageRef.current){
cbChangePageRef.current(0);
}
return Spotlight.focus(spotId); return Spotlight.focus(spotId);
} }
}; };
@@ -236,13 +210,15 @@ export default function HomePanel({ isOnTop }) {
let timer; let timer;
if (homeSpotlight) { if (homeSpotlight) {
const { y } = homeSpotlight; if(cbChangePageRef.current){
cbChangePageRef.current(homeSpotlight.pageIndex);
scrollTopBody({ y, animate: false }); }
timer = setTimeout(() => { timer = setTimeout(() => {
Spotlight.resume(); Spotlight.resume();
scrollTopBody({ y, animate: false }); //call again after some seconds if(cbChangePageRef.current){ //call again after some seconds
cbChangePageRef.current(homeSpotlight.pageIndex);
}
if (homeSpotlight.currentCatCd) { if (homeSpotlight.currentCatCd) {
Spotlight.focus("spotlightId-" + homeSpotlight.currentCatCd); Spotlight.focus("spotlightId-" + homeSpotlight.currentCatCd);
@@ -284,7 +260,7 @@ export default function HomePanel({ isOnTop }) {
currentSpot: currentSpot, currentSpot: currentSpot,
currentCatCd: targetSpotlightCatcd, currentCatCd: targetSpotlightCatcd,
currentCateName: targetSpotlightCateNm, currentCateName: targetSpotlightCateNm,
y: spotYoffsetRef.current, pageIndex: pageIndexRef.current,
}, },
}) })
); );
@@ -297,14 +273,121 @@ export default function HomePanel({ isOnTop }) {
} }
}, [firstSpot]); }, [firstSpot]);
const doSendLogGNB = useCallback((index)=>{
let nowMenu = null;
if(pageSpotIds[index]){
switch(pageSpotIds[index]){
case TEMPLATE_CODE_CONF.TOP:
nowMenu=LOG_MENU.HOME_TOP;
break;
case TEMPLATE_CODE_CONF.CATEGORY_ITEM:
nowMenu=LOG_MENU.HOME_CATEGORY;
break;
case TEMPLATE_CODE_CONF.ON_SALE:
nowMenu=LOG_MENU.HOME_ON_SALE;
break;
case TEMPLATE_CODE_CONF.POPULAR_SHOW:
nowMenu=LOG_MENU.HOME_POPULAR_SHOWS;
break;
case TEMPLATE_CODE_CONF.BEST_SELLER:
nowMenu=LOG_MENU.HOME_BEST_SELLER;
break;
}
}
if(nowMenu){
sendLogGNB(nowMenu);
}
},[pageSpotIds, sendLogGNB]);
const handleItemFocus = useCallback((index)=> () => {
doSendLogGNB(index);
},
[doSendLogGNB]
);
useEffect(() => { useEffect(() => {
if (homeTopDisplayInfos) { doSendLogGNB(pageIndex);
setSelectTemplate(homeTopDisplayInfos[0].shptmTmplCd); }, [pageIndex]);
const renderPageItem = useCallback(()=>{
return (
<>
{
sortedHomeLayoutInfo.map((el, idx)=>{
switch(el.shptmApphmDspyOptCd){
case TEMPLATE_CODE_CONF.TOP:{
return (
<HomeBanner
key={el.shptmApphmDspyOptCd}
spotlightId={el.shptmApphmDspyOptCd}
selectTemplate={selectTemplate}
firstSpot={firstSpot}
className={css.homeBannerWrap}
handleItemFocus={handleItemFocus(idx)}
/>
)
}
case TEMPLATE_CODE_CONF.CATEGORY_ITEM:{
return (
<SubCategory
key={el.shptmApphmDspyOptCd}
spotlightId={el.shptmApphmDspyOptCd}
catCd={cateCd}
cateNm={cateNm}
handleItemFocus={handleItemFocus(idx)}
/>
)
}
case TEMPLATE_CODE_CONF.ON_SALE:{
return (
<HomeOnSale
key={el.shptmApphmDspyOptCd}
spotlightId={el.shptmApphmDspyOptCd}
handleItemFocus={handleItemFocus(idx)}
/>
)
}
case TEMPLATE_CODE_CONF.POPULAR_SHOW:{
return (
<PopularShow
key={el.shptmApphmDspyOptCd}
spotlightId={el.shptmApphmDspyOptCd}
handleItemFocus={handleItemFocus(idx)}
/>
)
}
case TEMPLATE_CODE_CONF.BEST_SELLER:{
return (
<BestSeller
key={el.shptmApphmDspyOptCd}
spotlightId={el.shptmApphmDspyOptCd}
handleItemFocus={handleItemFocus(idx)}
/>
)
}
}
})
}
<TButton
className={css.tButton}
onClick={handleTopButtonClick}
size={null}
type={TYPES.topButton}
spotlightId={"home-top-btn"}
spotlightDisabled={btnActive}
aria-label="Move to Top, Button"
/>
</>
);
},[sortedHomeLayoutInfo, selectTemplate, cateCd,cateNm, handleItemFocus, handleTopButtonClick, btnActive]);
const onPageChanged = useCallback((index) => {
if(pageSpotIds.length <=index+1){
setArrowBottom(false);
}else{
setArrowBottom(true);
} }
if (homeLayoutInfo) { setPageIndex(index);
setHomeLayoutInfoDetail(homeLayoutInfo.homeLayoutInfo); }, [pageSpotIds]);
}
}, [homeTopDisplayInfos, homeLayoutInfo]);
return ( return (
<> <>
@@ -313,95 +396,20 @@ export default function HomePanel({ isOnTop }) {
<TBody <TBody
spotlightId={SpotlightIds.HOME_TBODY} spotlightId={SpotlightIds.HOME_TBODY}
className={css.tBody} className={css.tBody}
cbScrollTo={getScrollToBody} scrollable={false}
onScrollStop={_onScrollStop}
onScroll={_onScroll}
> >
<div data-marker="scroll-marker" ref={arrowRef} /> <TVerticalPagenator
<div className={css.orderableFlexContainer}> className={css.tVerticalPagenator}
{hasTemplateCodeWithValue( spotlightId={'home_verticalPagenator'}
homeLayoutInfoDetail, defaultPageIndex={homeSpotlight?.pageIndex ?? 0}
TEMPLATE_CODE_CONF.TOP disabled={!isOnTop}
) && pageSpotIds={pageSpotIds}
selectTemplate && ( onPageChanged={onPageChanged}
<HomeBanner scrollPositionRef={spotYoffsetRef}
selectTemplate={selectTemplate} cbChangePageRef={cbChangePageRef}
scrollTopBody={scrollTopBody} topMargin={36}>
order={getOrderByValue( {renderPageItem()}
homeLayoutInfoDetail, </TVerticalPagenator>
TEMPLATE_CODE_CONF.TOP
)}
firstSpot={firstSpot}
className={css.homeBannerWrap}
handleItemFocus={handleItemFocus}
/>
)}
{hasTemplateCodeWithValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.CATEGORY_ITEM
) && (
<SubCategory
order={getOrderByValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.CATEGORY_ITEM
)}
scrollTopBody={scrollTopBody}
catCd={cateCd}
cateNm={cateNm}
handleItemFocus={handleItemFocus}
/>
)}
{hasTemplateCodeWithValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.ON_SALE
) && (
<HomeOnSale
order={getOrderByValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.ON_SALE
)}
scrollTopBody={scrollTopBody}
handleItemFocus={handleItemFocus}
/>
)}
{hasTemplateCodeWithValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.POPULAR_SHOW
) && (
<PopularShow
order={getOrderByValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.POPULAR_SHOW
)}
scrollTopBody={scrollTopBody}
handleItemFocus={handleItemFocus}
/>
)}
{hasTemplateCodeWithValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.BEST_SELLER
) && (
<BestSeller
order={getOrderByValue(
homeLayoutInfoDetail,
TEMPLATE_CODE_CONF.BEST_SELLER
)}
scrollTopBody={scrollTopBody}
handleItemFocus={handleItemFocus}
/>
)}
</div>
<div ref={topRef}>
<TButton
className={css.tButton}
onClick={handleTopButtonClick}
size={null}
type={TYPES.topButton}
spotlightId={"home-top-btn"}
spotlightDisabled={btnActive}
aria-label={$L("TOP")}
/>
</div>
</TBody> </TBody>
{arrowBottom && ( {arrowBottom && (

View File

@@ -6,15 +6,14 @@
} }
.tBody { .tBody {
height: @globalHeight; height: @globalHeight;
.orderableFlexContainer { .tVerticalPagenator {
display: flex; height: 100vh !important;
flex-direction: column; >div>div>div{
> div {
margin-bottom: 70px; margin-bottom: 70px;
} }
} }
.tButton { .tButton {
margin-top: -10px; margin-top: 60px;
} }
} }
.arrow { .arrow {

View File

@@ -15,8 +15,7 @@ import TItemCard, {
import TScroller from "../../../components/TScroller/TScroller"; import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset"; import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo"; import useScrollTo from "../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../hooks/useScrollTopByDistance"; import { panel_names } from "../../../utils/Config";
import { LOG_MENU, panel_names } from "../../../utils/Config";
import { $L, scaleW } from "../../../utils/helperMethods"; import { $L, scaleW } from "../../../utils/helperMethods";
import css from "../PopularShow/PopularShow.module.less"; import css from "../PopularShow/PopularShow.module.less";
@@ -30,6 +29,7 @@ const PopularShow = ({
homeChk = true, homeChk = true,
order, order,
scrollTopBody, scrollTopBody,
spotlightId,
handleItemFocus, handleItemFocus,
...rest ...rest
}) => { }) => {
@@ -38,7 +38,6 @@ const PopularShow = ({
scrollLeft, scrollLeft,
true true
); );
const { scrollTopByDistance } = useScrollTopByDistance();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -101,20 +100,12 @@ const PopularShow = ({
if (cursorVisible) { if (cursorVisible) {
return; return;
} }
scrollTopByDistance(
`[data-marker="scroll-marker"]`,
`[data-title-index="homePopularShow"]`,
scrollTopBody,
36
);
}, },
[ [
cursorVisible, cursorVisible,
_handleItemFocus, _handleItemFocus,
handleScrollReset, handleScrollReset,
scrollTopBody, scrollTopBody
scrollTopByDistance,
] ]
); );
@@ -129,12 +120,12 @@ const PopularShow = ({
const _handleItemFocus = useCallback(() => { const _handleItemFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(LOG_MENU.HOME_POPULAR_SHOWS); handleItemFocus();
} }
}, [handleItemFocus]); }, [handleItemFocus]);
return ( return (
<Container className={css.popularShow} style={orderStyle}> <Container className={css.popularShow} style={orderStyle} spotlightId={spotlightId}>
<SectionTitle <SectionTitle
className={css.subTitle} className={css.subTitle}
title={$L("POPULAR SHOW")} title={$L("POPULAR SHOW")}

View File

@@ -7,7 +7,6 @@ import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDeco
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList"; import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollReset from "../../../../hooks/useScrollReset"; import useScrollReset from "../../../../hooks/useScrollReset";
import useScrollTo from "../../../../hooks/useScrollTo"; import useScrollTo from "../../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../../hooks/useScrollTopByDistance";
import CategoryNavItem from "../CategoryNav/CategoryNavItem/CategoryNavItem"; import CategoryNavItem from "../CategoryNav/CategoryNavItem/CategoryNavItem";
import css from "./CategoryNav.module.less"; import css from "./CategoryNav.module.less";
@@ -27,7 +26,6 @@ export default function CategoryNav({
const { cursorVisible } = useSelector((state) => state.common.appStatus); const { cursorVisible } = useSelector((state) => state.common.appStatus);
const { scrollLeft } = useScrollTo(); const { scrollLeft } = useScrollTo();
const { handleScrollReset } = useScrollReset(scrollLeft, true); const { handleScrollReset } = useScrollReset(scrollLeft, true);
const { scrollTopByDistance } = useScrollTopByDistance();
const handleFocus = useCallback( const handleFocus = useCallback(
(index) => () => { (index) => () => {
@@ -42,20 +40,12 @@ export default function CategoryNav({
if (cursorVisible) { if (cursorVisible) {
return; return;
} }
scrollTopByDistance(
`[data-marker="scroll-marker"]`,
`[data-title-index="subCategoryNav"]`,
scrollTopBody,
36
);
}, },
[ [
cursorVisible, cursorVisible,
handleItemFocus, handleItemFocus,
handleScrollReset, handleScrollReset,
scrollTopBody, scrollTopBody
scrollTopByDistance,
] ]
); );

View File

@@ -21,8 +21,7 @@ import TScroller from "../../../components/TScroller/TScroller";
import useLogService from "../../../hooks/useLogService"; import useLogService from "../../../hooks/useLogService";
import useScrollReset from "../../../hooks/useScrollReset"; import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo"; import useScrollTo from "../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../hooks/useScrollTopByDistance"; import { LOG_TP_NO, panel_names } from "../../../utils/Config";
import { LOG_MENU, LOG_TP_NO, panel_names } from "../../../utils/Config";
import CategoryNav from "../../HomePanel/SubCategory/CategoryNav/CategoryNav"; import CategoryNav from "../../HomePanel/SubCategory/CategoryNav/CategoryNav";
import css from "../../HomePanel/SubCategory/SubCategory.module.less"; import css from "../../HomePanel/SubCategory/SubCategory.module.less";
@@ -50,13 +49,14 @@ const SubCategory = ({
scrollTopBody, scrollTopBody,
catCd, catCd,
cateNm, cateNm,
spotlightId,
handleItemFocus, handleItemFocus,
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { sendLogCuration } = useLogService(); const { sendLogCuration } = useLogService();
const { scrollTopByDistance } = useScrollTopByDistance();
const { getScrollTo, scrollLeft } = useScrollTo(); const { getScrollTo, scrollLeft } = useScrollTo();
@@ -208,20 +208,12 @@ const SubCategory = ({
if (cursorVisible) { if (cursorVisible) {
return; return;
} }
scrollTopByDistance(
`[data-marker="scroll-marker"]`,
`[data-title-index="subCategoryNav"]`,
scrollTopBody,
36
);
}, },
[ [
cursorVisible, cursorVisible,
_handleItemFocus, _handleItemFocus,
handleScrollReset, handleScrollReset,
scrollTopBody, scrollTopBody
scrollTopByDistance,
] ]
); );
@@ -235,11 +227,11 @@ const SubCategory = ({
const _handleItemFocus = useCallback(() => { const _handleItemFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(LOG_MENU.HOME_CATEGORY); handleItemFocus();
} }
}, [handleItemFocus]); }, [handleItemFocus]);
return ( return (
<Container style={orderStyle}> <Container style={orderStyle} spotlightId={spotlightId}>
<ContainerBasic> <ContainerBasic>
<CategoryNav <CategoryNav
categoryInfos={categoryInfos} categoryInfos={categoryInfos}