From 2aa04e489a8ea4f77d5321cde794b61249f96288 Mon Sep 17 00:00:00 2001 From: yonghyon Date: Mon, 3 Jun 2024 22:52:15 +0900 Subject: [PATCH] pagenator --- .../TVerticalPagenator/TVerticalPagenator.jsx | 180 ++++++++++++------ .../views/HomePanel/BestSeller/BestSeller.jsx | 2 +- .../views/HomePanel/HomeBanner/HomeBanner.jsx | 2 +- .../views/HomePanel/HomeOnSale/HomeOnSale.jsx | 2 +- .../src/views/HomePanel/HomePanel.jsx | 134 ++++++------- .../src/views/HomePanel/HomePanel.module.less | 2 +- .../HomePanel/PopularShow/PopularShow.jsx | 2 +- .../HomePanel/SubCategory/SubCategory.jsx | 4 +- .../OnSaleContents/OnSaleContents.jsx | 2 +- .../src/views/OnSalePanel/OnSalePanel.jsx | 111 +++++------ 10 files changed, 241 insertions(+), 200 deletions(-) diff --git a/com.twin.app.shoptime/src/components/TVerticalPagenator/TVerticalPagenator.jsx b/com.twin.app.shoptime/src/components/TVerticalPagenator/TVerticalPagenator.jsx index 88755d2c..d0191dd1 100644 --- a/com.twin.app.shoptime/src/components/TVerticalPagenator/TVerticalPagenator.jsx +++ b/com.twin.app.shoptime/src/components/TVerticalPagenator/TVerticalPagenator.jsx @@ -1,6 +1,6 @@ /** * TVerticalPagenator - * + * checking data-wheel-point and data-spotlight-id * @module TVerticalPagenator */ @@ -13,35 +13,35 @@ import * as HelperMethods from "../../utils/helperMethods"; import usePrevious from '../../hooks/usePrevious'; import Spotlight from '@enact/spotlight'; +const DEBOUNCE_DELAY = 10; const TVerticalPagenator = ({ className, spotlightId, - defaultPageIndex=0, + defaultContainerId=null, enableFocusAction=true, children, disabled, - pageSpotIds = [], - onPageChanged, - visible = "hidden", - scrollPositionRef, + onScrollStatusChanged, + onFocusedContainerId, + scrollbarVisible = "hidden", cbChangePageRef, - scrollTop=false, topMargin=0, ...rest }) => { - const [pageIndex, setPageIndex] = useState(!defaultPageIndex ? 0: defaultPageIndex); + const [_defaultContainerId,_] = useState(defaultContainerId); const pagenatorRef = useRef(null); - const pageIndexRef = usePrevious(pageIndex); const isScrolling = useRef(false); - const scrollTopPositionRef = useRef(0); + const scrollTopPositionRef = useRef(-1); const scrollTo = useRef(null); const cursorVisible = useSelector((state) => state.common.appStatus.cursorVisible); const cursorVisibleRef = usePrevious(cursorVisible); const disabledRef = usePrevious(disabled); - const pageSpotIdsRef = usePrevious(pageSpotIds); + const topMarginRef = usePrevious(topMargin); const initialRenderedRef = useRef(false); - const lastScrollOffset = useRef(0); + const onFocusedContainerIdRef = usePrevious(onFocusedContainerId); + const scrollStatus = useRef("top"); + const lastEventTimeRef = useRef(0); useEffect(() => { if(spotlightId){ @@ -49,6 +49,39 @@ const TVerticalPagenator = ({ } }, [spotlightId]); + const findCurrentWheelPoint = useCallback((node)=>{ + if(pagenatorRef.current && pagenatorRef.current.contains(node)){ + return node.closest('[data-wheel-point="true"]'); + }else{ + return null; + } + },[]); + const findNextWheelPoint = useCallback((ev)=>{ + let matchedNode = null; + if(pagenatorRef.current){ + const nodes = pagenatorRef.current.querySelectorAll(`[data-wheel-point="true"]`); + for(let i=0; i{ + let matchedNode = null; + if(pagenatorRef.current){ + const nodes = pagenatorRef.current.querySelectorAll(`[data-wheel-point="true"]`); + for(let i=nodes.length-1; i>=0; i--){ + if(scrollTopPositionRef.current > (nodes[i].offsetTop-topMarginRef.current)){ + matchedNode = nodes[i]; + break; + } + } + } + return matchedNode; + },[]); const onFocusItem = useCallback((ev)=>{ if(!pagenatorRef.current){ return; @@ -58,24 +91,33 @@ const TVerticalPagenator = ({ 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 node = findCurrentWheelPoint(ev.target); + if(node){ + setTimeout(() => { + moveToNode(node, true, false); + }, 0); + } + },[moveToNode]); const handleWheel = useCallback((ev) => { + const now = Date.now(); + if (now - lastEventTimeRef.current < DEBOUNCE_DELAY) { + // ÀÏÁ¤ ½Ã°£ À̳»ÀÇ À̺¥Æ®´Â ¹«½Ã + return; + } + lastEventTimeRef.current = now; + if(pagenatorRef.current.contains(ev.target)){ ev.stopPropagation(); if(disabledRef.current){ return; } - let newIndex = ev.deltaY > 0 ? pageIndexRef.current+1: pageIndexRef.current -1; - newIndex = Math.max(newIndex, 0); - newIndex = Math.min(newIndex, pageSpotIdsRef.current.length-1); - setPageIndex(newIndex); + const node = ev.deltaY > 0 ? findNextWheelPoint():findPrevWheelPoint(); + if(node){ + setTimeout(() => { + moveToNode(node, true, true); + }, 0); + } } }, []); @@ -91,62 +133,90 @@ const TVerticalPagenator = ({ document.removeEventListener('wheel', handleWheel); }; }, []); - - const moveToPage = useCallback((index=0, animate=true, focus=enableFocusAction)=>{ - if(pageSpotIds[index]){ - const currentNode = document.querySelector( - `[data-spotlight-id="${pageSpotIds[index]}"]` - ); - if(currentNode){ - const distance = Math.round(currentNode.offsetTop - HelperMethods.scaleH(topMargin)); - const targetTop = distance >=0 ? distance:0; - scrollTo.current && scrollTo.current({ position:{x:0,y: targetTop}, animate: animate, focus: focus }); - if(focus){ - Spotlight.focus(currentNode); - } + const moveToNode = useCallback((node, animate=true, focus=enableFocusAction)=>{ + if(typeof node === 'string'){ + node = pagenatorRef.current.querySelector(`[data-spotlight-id="${node}"]`); + } + if(node){ + const distance = Math.round(node.offsetTop - HelperMethods.scaleH(topMarginRef.current)); + const targetTop = distance >=0 ? distance:0; + const currentSpot = Spotlight.getCurrent(); + if(scrollTopPositionRef.current === targetTop){ + return; + } + scrollTopPositionRef.current = targetTop; + scrollTo.current && scrollTo.current({ position:{x:0,y: targetTop}, animate: animate, focus: focus || !currentSpot, align: 'top' }); + if(focus || !currentSpot){ + Spotlight.focus(node); + } + if(onFocusedContainerIdRef.current){ + onFocusedContainerIdRef.current(node.getAttribute('data-spotlight-id')); } } - if(onPageChanged){ - onPageChanged(index); - } - },[topMargin, pageSpotIds, onPageChanged, enableFocusAction]); + },[enableFocusAction]); useEffect(() => { + let node = _defaultContainerId; + // console.log('yhcho TVerticalPagenator _defaultContainerId changed ', _defaultContainerId); + if(!_defaultContainerId){ + node = pagenatorRef.current.querySelector(`[data-wheel-point="true"]`); //to top + } if(!initialRenderedRef.current){ - moveToPage(pageIndex, false); + moveToNode(node, false); setTimeout(() => { - moveToPage(pageIndex, false); + moveToNode(node, false); }, 100); }else{ - moveToPage(pageIndex, true); + moveToNode(node, true); } initialRenderedRef.current = true; - }, [pageIndex]); + }, [_defaultContainerId]); useEffect(() => { if(cbChangePageRef){ - cbChangePageRef.current = (index, animate=false)=>{ - moveToPage(index, animate); - setPageIndex(index ? index : 0); + cbChangePageRef.current = (_spotlightId, animate=false)=>{ + let node = null; + if(!_spotlightId){ //to top + node = pagenatorRef.current.querySelector(`[data-wheel-point="true"]`); + }else{ + node = pagenatorRef.current.querySelector(`[data-spotlight-id="${_spotlightId}"]`); + } + if(node){ + setTimeout(() => { + moveToNode(node, animate); + }, 0); + } }; } - }, [cbChangePageRef, moveToPage]); + }, [cbChangePageRef]); const _onScrollStart = useCallback(() => { isScrolling.current = true; }, []); - const _onScrollStop = useCallback(() => { + const _onScrollStop = useCallback((ev) => { isScrolling.current = false; + scrollTopPositionRef.current = ev.scrollTop; }, []); const _onScroll = useCallback((ev) => { isScrolling.current = true; - scrollTopPositionRef.current = ev.scrollTop; - if(scrollPositionRef){ - scrollPositionRef.current = ev.scrollTop; + let prevScrollStatus = scrollStatus.current; + const scroller = pagenatorRef.current?.childNodes?.[0]; + if(scroller){ + if(ev.scrollTop <=0){ + scrollStatus.current = "top"; + }else if(ev.scrollTop + scroller.offsetHeight >= scroller.scrollHeight){ + scrollStatus.current = "end"; + }else{ + scrollStatus.current = "middle"; + } } - }, [scrollPositionRef]); + if(prevScrollStatus !== scrollStatus.current && onScrollStatusChanged){ + onScrollStatusChanged(scrollStatus.current); + } + }, [onScrollStatusChanged]); + if(!spotlightId){ console.warn('TVerticalPagenator has no spotlightId'); return null; @@ -157,10 +227,10 @@ const TVerticalPagenator = ({ id={spotlightId} spotlightId={spotlightId} className={classNames(css.verticalPagenator, className)} - focusableScrollbar={visible !== "hidden"} + focusableScrollbar={scrollbarVisible !== "hidden"} noScrollByWheel={true} noScrollByDrag={true} - verticalScrollbar={visible} + verticalScrollbar={scrollbarVisible} onFocus={onFocusItem} cbScrollTo={getScrollTo} onScroll={_onScroll} diff --git a/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx b/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx index 81fdf36e..85a75eb1 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx @@ -113,7 +113,7 @@ const BestSeller = ({ order, scrollTopBody, spotlightId, handleItemFocus }) => { }, [handleItemFocus]); return ( - + +
+
state.common.appStatus.webOSVersion ); - + const [focusedContainerId, setFocusedContainerId] = useState(homeSpotlight?.focusedContainerId); + const focusedContainerIdRef = usePrevious(focusedContainerId); + const cbChangePageRef = useRef(null); const selectTemplate = useMemo(()=>{ if (homeTopDisplayInfos) { return homeTopDisplayInfos[0].shptmTmplCd; @@ -155,7 +153,7 @@ export default function HomePanel({ isOnTop }) { spotId = "banner03"; } if(cbChangePageRef.current){ - cbChangePageRef.current(0); + cbChangePageRef.current(0, true); } return Spotlight.focus(spotId); } @@ -207,37 +205,7 @@ export default function HomePanel({ isOnTop }) { }, [dispatch, eventPopInfosData]); useEffect(() => { - let timer; - - if (homeSpotlight) { - if(cbChangePageRef.current){ - cbChangePageRef.current(homeSpotlight.pageIndex); - } - - timer = setTimeout(() => { - Spotlight.resume(); - if(cbChangePageRef.current){ //call again after some seconds - cbChangePageRef.current(homeSpotlight.pageIndex); - } - - if (homeSpotlight.currentCatCd) { - Spotlight.focus("spotlightId-" + homeSpotlight.currentCatCd); - } - - if (homeSpotlight.currentSpot) { - Spotlight.focus(homeSpotlight.currentSpot); - } - - setBtnActive(false); - setFirstSpot(true); - }, 300); - } else { - setBtnActive(false); - } - return () => { - clearTimeout(timer); - const c = Spotlight.getCurrent(); let targetSpotlightId = null; let targetSpotlightCatcd = null; @@ -260,7 +228,7 @@ export default function HomePanel({ isOnTop }) { currentSpot: currentSpot, currentCatCd: targetSpotlightCatcd, currentCateName: targetSpotlightCateNm, - pageIndex: pageIndexRef.current, + focusedContainerId: focusedContainerIdRef.current, }, }) ); @@ -273,40 +241,38 @@ export default function HomePanel({ isOnTop }) { } }, [firstSpot]); - const doSendLogGNB = useCallback((index)=>{ + const doSendLogGNB = useCallback((containerId)=>{ 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; - } + switch(containerId){ + 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); + const handleItemFocus = useCallback((containerId)=> () => { + doSendLogGNB(containerId); }, [doSendLogGNB] ); useEffect(() => { - doSendLogGNB(pageIndex); - }, [pageIndex]); + doSendLogGNB(focusedContainerId); + }, [focusedContainerId]); const renderPageItem = useCallback(()=>{ return ( @@ -322,7 +288,7 @@ export default function HomePanel({ isOnTop }) { selectTemplate={selectTemplate} firstSpot={firstSpot} className={css.homeBannerWrap} - handleItemFocus={handleItemFocus(idx)} + handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)} /> ) } @@ -333,7 +299,7 @@ export default function HomePanel({ isOnTop }) { spotlightId={el.shptmApphmDspyOptCd} catCd={cateCd} cateNm={cateNm} - handleItemFocus={handleItemFocus(idx)} + handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)} /> ) } @@ -342,7 +308,7 @@ export default function HomePanel({ isOnTop }) { ) } @@ -351,7 +317,7 @@ export default function HomePanel({ isOnTop }) { ) } @@ -360,7 +326,7 @@ export default function HomePanel({ isOnTop }) { ) } @@ -373,21 +339,38 @@ export default function HomePanel({ isOnTop }) { size={null} type={TYPES.topButton} spotlightId={"home-top-btn"} - spotlightDisabled={btnActive} + spotlightDisabled={btnDisabled} aria-label="Move to Top, Button" /> ); - },[sortedHomeLayoutInfo, selectTemplate, cateCd,cateNm, handleItemFocus, handleTopButtonClick, btnActive]); + },[sortedHomeLayoutInfo, selectTemplate, cateCd,cateNm, handleItemFocus, handleTopButtonClick, btnDisabled]); - const onPageChanged = useCallback((index) => { - if(pageSpotIds.length <=index+1){ + const onScrollStatusChanged = useCallback((status) => { + if(status === 'end'){ setArrowBottom(false); }else{ setArrowBottom(true); } - setPageIndex(index); - }, [pageSpotIds]); + }, []); + const onFocusedContainerId = useCallback((containerId) => { + setFocusedContainerId(containerId); + if (!firstSpot && homeSpotlight) { + setTimeout(() => { + Spotlight.resume(); + setFirstSpot(true); + if (homeSpotlight.currentCatCd) { + Spotlight.focus("spotlightId-" + homeSpotlight.currentCatCd); + } + if (homeSpotlight?.currentSpot) { + Spotlight.focus(homeSpotlight.currentSpot); + } + setBtnDisabled(false); + }, 0); + } else if(!firstSpot){ + setBtnDisabled(false); + } + }, [homeSpotlight, firstSpot]); return ( <> @@ -401,11 +384,10 @@ export default function HomePanel({ isOnTop }) { {renderPageItem()} diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less index b38c89ca..2db284f7 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less +++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less @@ -13,7 +13,7 @@ } } .tButton { - margin-top: 60px; + margin-top: -20px; } } .arrow { diff --git a/com.twin.app.shoptime/src/views/HomePanel/PopularShow/PopularShow.jsx b/com.twin.app.shoptime/src/views/HomePanel/PopularShow/PopularShow.jsx index 22bdde1b..7b210f9f 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/PopularShow/PopularShow.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/PopularShow/PopularShow.jsx @@ -125,7 +125,7 @@ const PopularShow = ({ }, [handleItemFocus]); return ( - + + + 4; const cbChangePageRef = useRef(null); - const pageIndexRef = useRef(0); + const focusedContainerIdRef = useRef(0); useEffect(() => { sendLogGNB(LOG_MENU.ON_SALE); @@ -197,7 +197,7 @@ export default function OnSalePanel({ panelInfo }) { isReadyForInitialFocusTarget, panelInfo, previousPanelIsDetail, - previousPanelIsHome, + previousPanelIsHome ]); useEffect(() => { @@ -277,7 +277,7 @@ export default function OnSalePanel({ panelInfo }) { exprOrd, lgCatCd, targetId, - pageIndex: pageIndexRef.current, + focusedContainerId: focusedContainerIdRef.current, }, }) ); @@ -286,33 +286,22 @@ export default function OnSalePanel({ panelInfo }) { }, [dispatch]); const handleTopButtonClick = useCallback(() => { - if (cbChangePageRef.current) { - if (!firstFocusableTarget) { - cbChangePageRef.current(0, true); - return; - } - - setIsTopButtonClicked(true); - - setTimeout(() => { - Spotlight.focus(firstFocusableTarget); - setIsTopButtonClicked(false); - }); + if(cbChangePageRef.current){ + cbChangePageRef.current(0, true); } + if (!firstFocusableTarget) { + return; + } + setIsTopButtonClicked(true); + + setTimeout(() => { + Spotlight.focus(firstFocusableTarget); + setIsTopButtonClicked(false); + }); }, [firstFocusableTarget]); - const pageSpotIds = useMemo(() => { - const spots = []; - if (saleInfos) { - for (let i = 0; i < saleInfos.length; i++) { - spots.push("sale-info-" + i); - } - } - return spots; - }, [saleInfos]); - - const onPageChanged = useCallback((index) => { - pageIndexRef.current = index; + const onFocusedContainerId = useCallback((containerId) => { + focusedContainerIdRef.current = containerId; }, []); const onSaleNavClicked = useCallback((ev) => { @@ -339,40 +328,40 @@ export default function OnSalePanel({ panelInfo }) { /> - {saleInfos && ( - - {saleInfos.map( - ({ saleNm, saleProductInfos }, contentsIndex) => ( - - ) - )} - {saleInfos.length > 1 && ( - - )} - - )} + {saleInfos && + + <> + {saleInfos.map( + ({ saleNm, saleProductInfos }, contentsIndex) => ( + + ) + )} + {saleInfos.length > 1 && ( + + )} + + + } )}