search panel focus

This commit is contained in:
yonghyon
2024-06-13 20:49:57 +09:00
parent b5e0a650d1
commit 665e56ab24
10 changed files with 142 additions and 89 deletions

View File

@@ -213,12 +213,6 @@ const TVerticalPagenator = forwardRef(
focus: focus || !currentSpot,
align: "top",
});
// [2024.06.04] 베너 탑인 경우에는, LIVE,VOD 로 포커스 가도록 수정
if (node.getAttribute("data-spotlight-id") !== "DSP00101") {
if (focus || !currentSpot) {
Spotlight.focus(node);
}
}
if (onFocusedContainerIdRef.current) {
if (!node.getAttribute("data-spotlight-id")) {
console.warn("TVerticalPagenator should have spotlight id");

View File

@@ -16,8 +16,10 @@ import {
} from '../../utils/helperMethods';
import AutoScrollArea, { POSITION } from '../AutoScrollArea/AutoScrollArea';
import css from './TVirtualGridList.module.less';
import Spotlight from '@enact/spotlight';
export default function TVirtualGridList({
defaultSpotlightId,
dataSize,
direction = "vertical",
className,
@@ -43,6 +45,7 @@ export default function TVirtualGridList({
const scrollPosition = useRef("top");
const scrollToRef = useRef(null);
const defaultFocusTargetRef = useRef(null);
const scrollHorizontalPos = useRef(0);
const scrollVerticalPos = useRef(0);
const visibleIndexRef = useRef({firstVisibleIndex:0, lastVisibleIndex:0});
@@ -58,6 +61,13 @@ export default function TVirtualGridList({
useEffect(() => {
const listRef = gridlistParentRef.current.childNodes[0];
if(spotlightId &&defaultSpotlightId && defaultSpotlightId.indexOf(spotlightId) === 0){
let matched = defaultSpotlightId.match(/\d+$/); //find index
if(matched){
defaultFocusTargetRef.current = {spotlightId: defaultSpotlightId, index: matched[0]};
scrollToRef.current({index: matched[0], animate: false, focus: true});
}
}
if(direction === 'vertical'){
listRef.addEventListener("wheel", handleWheel, true);
}
@@ -86,9 +96,14 @@ export default function TVirtualGridList({
if (onScrollStop) {
onScrollStop(e);
}
isScrolling.current = false;
if(defaultFocusTargetRef.current){
if(defaultFocusTargetRef.current.index >= e.moreInfo.firstVisibleIndex
&& defaultFocusTargetRef.current.index <= e.moreInfo.lastVisibleIndex){
Spotlight.focus(defaultFocusTargetRef.current.spotlightId);
defaultFocusTargetRef.current = null;
}
}
if (e.reachedEdgeInfo) {
if (e.reachedEdgeInfo.top) {
scrollPosition.current = "top";

View File

@@ -21,6 +21,12 @@ export const SpotlightIds = {
LIST_PLAYER: "list_player",
LIST_PLAYER2: "list_player2",
// SearchPanel
SEARCH_THEME: "search_theme",
SEARCH_SHOW: "search_show",
SEARCH_ITEM: "search_item",
SEARCH_BESTSELLER: "search_bestseller",
//pin Code Popup
PINCODE_CONTAINER: "pincodeContainer",
};

View File

@@ -72,7 +72,7 @@ export default function HomeBanner({
}
return "banner" + targetIndex;
}
return "banner0";
return null;
}, [bannerDataList]);
useEffect(() => {
@@ -87,7 +87,7 @@ export default function HomeBanner({
(index, isHorizontal) => {
const data = bannerDataList?.[index] ?? {};
return (
<div className={!isHorizontal && css.imgBox}>
<div className={!isHorizontal ? css.imgBox: undefined}>
{data.shptmDspyTpNm === "Rolling" ? (
<Rolling
bannerData={data}

View File

@@ -14,6 +14,7 @@ import { LOG_MENU, panel_names } from "../../../utils/Config";
import { $L } from "../../../utils/helperMethods";
import css from "./NoSearchResults.module.less";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { SpotlightIds } from "../../../utils/SpotlightIds";
const ContainerBasic = SpotlightContainerDecorator(
{ enterTo: null },
"div"
@@ -84,7 +85,7 @@ export default function NoSearchResults({ handleItemFocus }) {
</div>
<p>{$L("SORRY, NO RESULTS MATCHING YOUR SEARCH")}</p>
</div>
<ContainerBasic className={css.bestSellerWrap} data-wheel-point={true}>
<ContainerBasic className={css.bestSellerWrap} data-wheel-point={true} spotlightId={SpotlightIds.SEARCH_BESTSELLER}>
<SectionTitle title={$L("BEST SELLER")} />
<TGrid>
{bestSellerDatas &&

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
@@ -9,7 +9,6 @@ import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDeco
import { popPanel, updatePanel } from "../../actions/panelActions";
import { getSearch, resetSearch } from "../../actions/searchActions";
import TBody from "../../components/TBody/TBody";
import TButton from "../../components/TButton/TButton";
import TInput, { ICONS, KINDS } from "../../components/TInput/TInput";
import TPanel from "../../components/TPanel/TPanel";
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
@@ -20,6 +19,8 @@ import NoSearchResults from "./NoSearchResults/NoSearchResults";
import RecommendedKeywords from "./RecommendedKeywords/RecommendedKeywords";
import css from "./SearchPanel.module.less";
import SearchResults from "./SearchResults/SearchResults";
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
import { SpotlightIds } from "../../utils/SpotlightIds";
const ContainerBasic = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
@@ -30,7 +31,7 @@ const ITEMS_PER_PAGE = 9;
export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const { sendLogGNB } = useLogService();
const dispatch = useDispatch();
const [firstSpot, setFirstSpot] = useState(false);
const recommandedKeywords = useSelector(
(state) => state.myPage.recommandedKeywordData.data?.keywords
);
@@ -96,10 +97,17 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}
}, []);
const clearAllContainerHistory = useCallback(()=>{
setContainerLastFocusedElement(null, [SpotlightIds.SEARCH_THEME]);
setContainerLastFocusedElement(null, [SpotlightIds.SEARCH_SHOW]);
setContainerLastFocusedElement(null, [SpotlightIds.SEARCH_ITEM]);
setContainerLastFocusedElement(null, [SpotlightIds.SEARCH_BESTSELLER]);
},[]);
const handleSearchSubmit = useCallback(
(query) => {
clearAllContainerHistory();
if (!searchPerformed && !query) return;
if (query.trim()) {
dispatch(
getSearch({
@@ -137,14 +145,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
useEffect(() => {
const focusJobValue = focusJob.current;
if (panelInfo && isOnTop) {
if (panelInfo.currentSpot) {
console.log("chw", panelInfo.currentSpot);
Spotlight.focus(panelInfo.currentSpot);
focusJobValue.start(() => Spotlight.focus(panelInfo.currentSpot));
const currentFocusedItem = Spotlight.getCurrent();
if (panelInfo.currentSpot && firstSpot) {
Spotlight.focus(panel_names.SEARCH_PANEL);
}
}
}, [panelInfo, isOnTop]);
@@ -194,7 +197,25 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
);
const onFocusedContainerId = useCallback((containerId) => {
setFocusedContainerId(containerId);
}, []);
if (!firstSpot) {
setTimeout(() => {
Spotlight.resume();
setFirstSpot(true);
if (panelInfo.currentSpot) {
Spotlight.focus(panelInfo.currentSpot);
}
}, 0);
}
}, [panelInfo, firstSpot]);
const panelInfoFall = useMemo(()=>{
const newPanelInfo = {...panelInfo};
if(firstSpot){
newPanelInfo.currentSpot = null;
}
return newPanelInfo;
},[panelInfo, firstSpot]);
return (
<TPanel
className={css.container}
@@ -206,6 +227,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
scrollable={false}
spotlightDisabled={!isOnTop}
>
<ContainerBasic>
<TVerticalPagenator
className={css.tVerticalPagenator}
spotlightId={"search_verticalPagenator"}
@@ -216,47 +238,49 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
cbChangePageRef={cbChangePageRef}
topMargin={36}
>
<ContainerBasic
className={css.inputContainer}
data-wheel-point={true}
>
<TInput
className={css.inputBox}
kind={KINDS.withIcon}
icon={ICONS.search}
value={searchQuery}
onChange={handleSearchChange}
onIconClick={() => handleSearchSubmit(searchQuery)}
onKeyDown={handleKeydown}
forcedSpotlight="first-keyword-button"
spotlightId={"search-input-box"}
/>
</ContainerBasic>
{searchPerformed && searchQuery !== null ? (
Object.keys(searchDatas).length > 0 ? (
<SearchResults
panelInfo={panelInfo}
searchQueryRef={searchQueryRef}
isRecommendedSearchRef={isRecommendedSearchRef}
/>
) : (
<NoSearchResults handleItemFocus={handleItemFocus} />
)
) : (
<ContainerBasic>
<RecommendedKeywords
keywords={paginatedKeywords}
onPrev={handlePrev}
onNext={handleNext}
hasPrevPage={hasPrevPage}
hasNextPage={hasNextPage}
handleSearchSubmit={handleSearchSubmit}
isRecommendedSearchRef={isRecommendedSearchRef}
<ContainerBasic
className={css.inputContainer}
data-wheel-point={true}
spotlightId={'search-input-layer'}
>
<TInput
className={css.inputBox}
kind={KINDS.withIcon}
icon={ICONS.search}
value={searchQuery}
onChange={handleSearchChange}
onIconClick={() => handleSearchSubmit(searchQuery)}
onKeyDown={handleKeydown}
forcedSpotlight="first-keyword-button"
spotlightId={"search-input-box"}
/>
</ContainerBasic>
)}
</TVerticalPagenator>
{searchPerformed && searchQuery !== null ? (
Object.keys(searchDatas).length > 0 ? (
<SearchResults
panelInfo={panelInfoFall}
searchQueryRef={searchQueryRef}
isRecommendedSearchRef={isRecommendedSearchRef}
/>
) : (
<NoSearchResults handleItemFocus={handleItemFocus} />
)
) : (
<ContainerBasic>
<RecommendedKeywords
keywords={paginatedKeywords}
onPrev={handlePrev}
onNext={handleNext}
hasPrevPage={hasPrevPage}
hasNextPage={hasNextPage}
handleSearchSubmit={handleSearchSubmit}
isRecommendedSearchRef={isRecommendedSearchRef}
/>
</ContainerBasic>
)}
</TVerticalPagenator>
</ContainerBasic>
</TBody>
</TPanel>
);

View File

@@ -6,6 +6,10 @@
.tBody {
height: 100%;
>div{
width: 100%;
height: 100%;
}
.focusedContainerId{
height: 100%;
}

View File

@@ -10,6 +10,7 @@ import useLogService from "../../../hooks/useLogService";
import { $L } from "../../../utils/helperMethods";
import css from "./SearchResults.module.less";
import SearchResultsType from "./SearchResultsType";
import { SpotlightIds } from "../../../utils/SpotlightIds";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
@@ -55,11 +56,11 @@ export default memo(function SearchResults({
spotJobValue.start(() => {
if (searchDatas) {
if (searchDatas?.theme?.length > 0) {
Spotlight.focus("searchThemeItem0");
Spotlight.focus(SpotlightIds.SEARCH_THEME);
} else if (searchDatas?.show?.length > 0) {
Spotlight.focus("searchShowItem0");
Spotlight.focus(SpotlightIds.SEARCH_SHOW);
} else {
Spotlight.focus("searchItem0");
Spotlight.focus(SpotlightIds.SEARCH_ITEM);
}
}
});
@@ -99,7 +100,7 @@ export default memo(function SearchResults({
count={count}
data={data}
idx={index}
key={title + index}
key={category + "searchResult"}
panelInfo={panelInfo}
searchQueryRef={searchQueryRef}
sectionTitle={title}

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
@@ -16,6 +16,7 @@ import SearchItemCard from "./SearchCard/SearchItemCard";
import SearchShowCard from "./SearchCard/SearchShowCard";
import SearchThemeCard from "./SearchCard/SearchThemeCard";
import css from "./SearchResultsType.module.less";
import { SpotlightIds } from "../../../utils/SpotlightIds";
const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: "last-focused" },
@@ -59,10 +60,10 @@ export default function SearchResultsType({
const dataRef = usePrevious(data);
useEffect(() => {
if (initPerformed) {
if(data && !panelInfo.currentSpot){
scrollLeft();
}
}, [initPerformed]);
}, [data]);
useEffect(() => {
const scrollLeftJobValue = scrollLeftJob.current;
@@ -83,7 +84,7 @@ export default function SearchResultsType({
const onScroll = useCallback(
(ev) => {
const { lastVisibleIndex } = ev.moreInfo;
if (dataRef.current.length >= SEARCH_DATA_MAX_RESULTS_LIMIT) {
if (dataRef.current && dataRef.current.length >= SEARCH_DATA_MAX_RESULTS_LIMIT) {
const nextSearchIndex =
(Math.floor(lastVisibleIndex / SEARCH_DATA_MAX_RESULTS_LIMIT) + 1) *
SEARCH_DATA_MAX_RESULTS_LIMIT +
@@ -117,11 +118,11 @@ export default function SearchResultsType({
<SearchThemeCard
contentId={contentId}
idx={idx}
key={"theme" + index}
key={category + index}
keyword={keyword}
patncNm={partnerName}
searchQueryRef={searchQueryRef}
spotlightId={"searchThemeItem" + index}
spotlightId={SpotlightIds.SEARCH_THEME+ index}
title={title}
type={"theme"}
{...rest}
@@ -146,11 +147,11 @@ export default function SearchResultsType({
<SearchShowCard
contentId={contentId}
idx={idx}
key={"show" + index}
key={category + index}
liveFlag={liveFlag}
patncNm={partnerName}
searchQueryRef={searchQueryRef}
spotlightId={"searchShowItem" + index}
spotlightId={SpotlightIds.SEARCH_SHOW+ index}
thumbnail={thumbnail}
title={title}
{...rest}
@@ -176,11 +177,11 @@ export default function SearchResultsType({
contentId={contentId}
dcPrice={dcPrice}
idx={idx}
key={"item" + index}
key={category + index}
patncNm={partnerName}
price={price}
searchQueryRef={searchQueryRef}
spotlightId={"searchItem" + index}
spotlightId={SpotlightIds.SEARCH_ITEM+ index}
thumbnail={thumbnail}
title={title}
length={dataSize}
@@ -196,23 +197,33 @@ export default function SearchResultsType({
[category, data, idx, searchQueryRef]
);
const gridListId = useMemo(()=>{
switch(category){
case 'theme':
return SpotlightIds.SEARCH_THEME;
case 'show':
return SpotlightIds.SEARCH_SHOW;
case 'item':
return SpotlightIds.SEARCH_ITEM;
}
},[category]);
const { itemWidth, itemHeight, spacing } = ITEM_SIZE[category] || {};
return (
<Container data-wheel-point={true}>
<Container data-wheel-point={true} spotlightId={'searchresult_section_'+category}>
<SectionTitle
className={css.sectionTitle}
title={sectionTitle}
itemCount={count ? count : null}
label={sectionTitle + ", Heading 2"}
/>
<Container
className={classNames(css.container, category && css[category])}
>
<TVirtualGridList
defaultSpotlightId={panelInfo.currentSpot}
key={sectionTitle}
spotlightId={gridListId}
cbScrollTo={getScrollTo}
className={css.grid}
className={classNames(css.container, category && css[category], css.grid)}
dataSize={count ? count : 0}
renderItem={renderItem}
direction="horizontal"
@@ -223,6 +234,5 @@ export default function SearchResultsType({
onScroll={onScroll}
/>
</Container>
</Container>
);
}

View File

@@ -5,18 +5,16 @@
padding-left: 60px;
}
.container {
> div {
.flex();
.flex();
margin: 30px 0 50px;
width: 100%;
}
.grid {
&.grid {
padding-right: 60px;
padding-left: 60px;
}
&.theme {
.grid {
&.grid {
> div {
height: 141px;
}
@@ -24,7 +22,7 @@
}
&.show {
.grid {
&.grid {
> div {
height: 240px;
}
@@ -32,7 +30,7 @@
}
&.item {
.grid {
&.grid {
> div {
height: 150px;
}