545 lines
14 KiB
JavaScript
545 lines
14 KiB
JavaScript
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
|
|
import classNames from "classnames";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
|
|
import { panel_names } from "../../utils/Config";
|
|
import TabItem from "./TabItem";
|
|
import css from "./TabLayout.module.less";
|
|
//enact
|
|
import Skinnable from "@enact/sandstone/Skinnable";
|
|
import Spotlight from "@enact/spotlight";
|
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
|
import { Cancelable } from "@enact/ui/Cancelable";
|
|
//아이콘
|
|
import { Job } from "@enact/core/util";
|
|
|
|
//이미지
|
|
import shoptimeFullIcon from "../../../assets/icons/ic-lnb-logo-shoptime@3x.png";
|
|
import shopTimeIcon from "../../../assets/icons/ic-lnb-shoptime-symbol@3x.png";
|
|
import CartIcon from "./iconComponents/CartIcon";
|
|
import CategoryIcon from "./iconComponents/CategoryIcon";
|
|
import FeaturedBrandIcon from "./iconComponents/FeaturedBrandIcon";
|
|
import HomeIcon from "./iconComponents/HomeIcon";
|
|
import HotPicksIcon from "./iconComponents/HotPicksIcon";
|
|
import MyPageIcon from "./iconComponents/MyPageIcon";
|
|
import OnSaleIcon from "./iconComponents/OnSaleIcon";
|
|
import SearchIcon from "./iconComponents/SearchIcon";
|
|
import TrendingNowIcon from "./iconComponents/TrendingNowIcon";
|
|
|
|
//이미지
|
|
import { resetPanels } from "../../features/panels/panelsSlice";
|
|
|
|
const Container = SpotlightContainerDecorator(
|
|
{ enterTo: "default-element" },
|
|
"div"
|
|
);
|
|
|
|
const MainContainer = SpotlightContainerDecorator(
|
|
{ enterTo: "last-focused", continue5WayHold: true },
|
|
"div"
|
|
);
|
|
|
|
const CancelableDiv = Cancelable(
|
|
{ modal: true, onCancel: "handleCancel" },
|
|
Skinnable(Container)
|
|
);
|
|
|
|
class TabMenuItem {
|
|
constructor(icons = "", title = "", target, children = []) {
|
|
this.icons = icons;
|
|
this.title = title;
|
|
this.target = target;
|
|
this.children = []; //TabMenuItem
|
|
if (children && children.length > 0) {
|
|
for (let i = 0; i < children.length; i++) {
|
|
const tabmenu = new TabMenuItem(
|
|
children[i].icons,
|
|
children[i].title,
|
|
children[i].target,
|
|
children[i].children
|
|
);
|
|
this.children.push(tabmenu);
|
|
}
|
|
}
|
|
}
|
|
hasChildren = () => {
|
|
return this.children.length > 0;
|
|
};
|
|
getChildren = () => {
|
|
return this.children;
|
|
};
|
|
}
|
|
|
|
const deActivateTabJabFunc = (func) => {
|
|
func();
|
|
};
|
|
let deActivateTabJob = new Job(deActivateTabJabFunc, 2000);
|
|
|
|
const COLLABSED_MAIN = 0;
|
|
const ACTIVATED_MAIN = 1;
|
|
const ACTIVATED_SUB = 2;
|
|
const EXTRA_AREA = 3;
|
|
|
|
const MAIN_TITLE = 0;
|
|
const SUB_TITLE = 1;
|
|
|
|
const PANELS_HAS_TAB = [
|
|
panel_names.CART_PANEL,
|
|
panel_names.CATEGORY_PANEL,
|
|
panel_names.FEATURED_BRANDS_PANEL,
|
|
panel_names.HOME_PANEL,
|
|
panel_names.HOT_PICKS_PANEL,
|
|
panel_names.MY_PAGE_PANEL,
|
|
panel_names.ON_SALE_PANEL,
|
|
panel_names.SEARCH_PANEL,
|
|
panel_names.TRENDING_NOW_PANEL,
|
|
];
|
|
|
|
export default function TabLayout({ topPanelName, onTabActivated }) {
|
|
const dispatch = useDispatch();
|
|
const [mainExpanded, setMainExpanded] = useState(false);
|
|
const [mainSelectedIndex, setMainSelectedIndex] = useState(-1);
|
|
const [tabs, setTabs] = useState([]);
|
|
const [tabFocused, setTabFocused] = useState([false, false, false]); //COLLABSED_MAIN, ACTIVATED_MAIN, ACTIVATED_SUB
|
|
const panelSwitching = useRef(null);
|
|
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
|
const data = useSelector((state) => state.home.menuData?.data);
|
|
|
|
const menuItems = useMemo(
|
|
() => [
|
|
{
|
|
icons: CategoryIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.CATEGORY_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: MyPageIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.MY_PAGE_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: SearchIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "12323232",
|
|
target: [{ name: panel_names.SEARCH_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: HomeIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "43534543",
|
|
target: [{ name: panel_names.HOME_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: FeaturedBrandIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "dsadasd",
|
|
target: [{ name: panel_names.FEATURED_BRANDS_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: OnSaleIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.ON_SALE_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: TrendingNowIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.TRENDING_NOW_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: HotPicksIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.HOT_PICKS_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
icons: CartIcon,
|
|
title: "",
|
|
children: [
|
|
{
|
|
title: "",
|
|
target: [{ name: panel_names.CART_PANEL }],
|
|
},
|
|
],
|
|
},
|
|
|
|
//메뉴 추가 필요 20240112 chw
|
|
],
|
|
[data]
|
|
);
|
|
|
|
const getMenuData = (type) => {
|
|
let result = [];
|
|
|
|
switch (type) {
|
|
case "gnb":
|
|
data?.gnb.map((item) => {
|
|
result.push(item.menuNm);
|
|
});
|
|
console.log("#result", result);
|
|
return result;
|
|
case "homeCategory":
|
|
return "123123";
|
|
case "msgInfos":
|
|
return result;
|
|
case "mypage":
|
|
return result;
|
|
case "shortCategory":
|
|
return result;
|
|
case "shortFeaturedBrands":
|
|
return result;
|
|
}
|
|
};
|
|
|
|
const dataDivide = () => {
|
|
if (data) {
|
|
// console.log("data", data);
|
|
// Object.keys(data).map((key) => {
|
|
// title = getMenuData(key);
|
|
// return title;
|
|
// });
|
|
|
|
const title = getMenuData("gnb");
|
|
// const subTitle = getMenuData(menu);
|
|
|
|
for (let i = 0; i < menuItems.length; i++) {
|
|
menuItems[i].title = title[i];
|
|
menuItems[i].children.title = title[i];
|
|
}
|
|
}
|
|
console.log("#menuItems", menuItems);
|
|
return menuItems;
|
|
};
|
|
|
|
const makeTabmenu = useCallback(() => {
|
|
const t = [];
|
|
|
|
for (let i = 0; i < menuItems.length; i++) {
|
|
const tabmenu = new TabMenuItem(
|
|
menuItems[i].icons,
|
|
menuItems[i].title,
|
|
menuItems[i].target,
|
|
menuItems[i].children
|
|
);
|
|
t.push(tabmenu);
|
|
}
|
|
|
|
return t;
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
dataDivide();
|
|
setTabs(makeTabmenu());
|
|
}, [data]);
|
|
|
|
const deActivateTab = useCallback(() => {
|
|
setTabFocused([false, false, false, false]);
|
|
setMainSelectedIndex(-1);
|
|
setMainExpanded(false);
|
|
}, []);
|
|
|
|
const onTabHasFocus = useCallback(
|
|
(type) => (event) => {
|
|
switch (type) {
|
|
case COLLABSED_MAIN:
|
|
case ACTIVATED_MAIN: {
|
|
if (!cursorVisible) {
|
|
const parent = event.target.parentNode;
|
|
const children = parent.childNodes;
|
|
const index = Array.prototype.indexOf.call(children, event.target);
|
|
setMainExpanded(true);
|
|
setMainSelectedIndex(index);
|
|
} else {
|
|
if (!panelSwitching.current) {
|
|
setMainExpanded(true);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ACTIVATED_SUB: {
|
|
setMainExpanded(false);
|
|
break;
|
|
}
|
|
case EXTRA_AREA: {
|
|
if (cursorVisible) {
|
|
deActivateTabJob.start(deActivateTab);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
setTabFocused((prevState) => {
|
|
const prev = [...prevState];
|
|
prev[type] = true;
|
|
return prev;
|
|
});
|
|
},
|
|
[cursorVisible, deActivateTab]
|
|
);
|
|
|
|
const onTabBlur = useCallback(
|
|
(type) => (event) => {
|
|
switch (type) {
|
|
case ACTIVATED_MAIN: {
|
|
if (!cursorVisible) {
|
|
setMainExpanded(false);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ACTIVATED_SUB: {
|
|
if (!cursorVisible) {
|
|
}
|
|
break;
|
|
}
|
|
case EXTRA_AREA: {
|
|
if (cursorVisible) {
|
|
deActivateTabJob.stop();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
setTabFocused((prevState) => {
|
|
const prev = [...prevState];
|
|
prev[type] = true;
|
|
return prev;
|
|
});
|
|
},
|
|
[cursorVisible]
|
|
);
|
|
|
|
const handleNavigation = useCallback(
|
|
({ index, target }) => {
|
|
setMainSelectedIndex(index);
|
|
if (target) {
|
|
dispatch(resetPanels(target));
|
|
deActivateTab();
|
|
// panelSwitching.current = true;
|
|
// panelSwitchingJob.start(panelSwitching);
|
|
} else if (cursorVisible) {
|
|
setMainExpanded(true);
|
|
}
|
|
},
|
|
[deActivateTab, dispatch]
|
|
);
|
|
|
|
const onClickSubItem = useCallback(
|
|
({ index, target }) => {
|
|
if (target) {
|
|
dispatch(resetPanels(target));
|
|
deActivateTab();
|
|
panelSwitching.current = true;
|
|
// panelSwitchingJob.start(panelSwitching);
|
|
}
|
|
},
|
|
[dispatch, deActivateTab]
|
|
);
|
|
const onClickExtraArea = useCallback(
|
|
({ index, target }) => {
|
|
deActivateTabJob.startAfter(100, deActivateTab);
|
|
},
|
|
[dispatch, deActivateTab]
|
|
);
|
|
|
|
const tabActivated = useMemo(() => {
|
|
return mainExpanded || mainSelectedIndex >= 0;
|
|
}, [mainExpanded, mainSelectedIndex]);
|
|
|
|
const showTab = useMemo(() => {
|
|
if (!topPanelName || PANELS_HAS_TAB.indexOf(topPanelName) >= 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [topPanelName]);
|
|
|
|
const showSubTab = useMemo(() => {
|
|
if (
|
|
tabActivated &&
|
|
tabs[mainSelectedIndex] &&
|
|
tabs[mainSelectedIndex].hasChildren()
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [tabActivated, tabs, mainSelectedIndex]);
|
|
|
|
const backKeyHandler = useCallback(
|
|
(ev) => {
|
|
if (tabActivated) {
|
|
deActivateTab();
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
return true;
|
|
}
|
|
},
|
|
[tabActivated, deActivateTab]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (tabActivated) {
|
|
setTimeout(() => {
|
|
Spotlight.focus("activatedMain");
|
|
}, 0);
|
|
}
|
|
if (onTabActivated) {
|
|
onTabActivated(tabActivated && showTab);
|
|
}
|
|
}, [tabActivated, showTab]);
|
|
|
|
useEffect(() => {}, [showSubTab, mainSelectedIndex]);
|
|
|
|
if (!showTab) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className={classNames(css.tabLayoutWrap, !tabActivated && css.hide)}>
|
|
{/* collabsed Main */}
|
|
<Container
|
|
className={css.tabWrap}
|
|
onFocus={onTabHasFocus(COLLABSED_MAIN)}
|
|
onMouseOver={onTabHasFocus(COLLABSED_MAIN)}
|
|
onBlur={onTabBlur(COLLABSED_MAIN)}
|
|
onMouseLeave={onTabBlur(COLLABSED_MAIN)}
|
|
>
|
|
<img className={css.logo} src={shopTimeIcon} alt="" />
|
|
{tabs.map((item, index) => (
|
|
<TabItem
|
|
key={"tabitem" + index}
|
|
expanded={false}
|
|
selected={mainSelectedIndex === index}
|
|
index={index}
|
|
onClick={handleNavigation}
|
|
icons={item.icons}
|
|
/>
|
|
))}
|
|
</Container>
|
|
{
|
|
<CancelableDiv
|
|
spotlightRestrict="self-only"
|
|
spotlightId="activatedMain"
|
|
className={classNames(
|
|
css.expandedRootContainer,
|
|
!tabActivated && css.hide
|
|
)}
|
|
handleCancel={backKeyHandler}
|
|
>
|
|
{/* expanded Main */}
|
|
<MainContainer
|
|
className={classNames(css.tabWrap, mainExpanded && css.expanded)}
|
|
onFocus={onTabHasFocus(ACTIVATED_MAIN)}
|
|
onMouseOver={onTabHasFocus(COLLABSED_MAIN)}
|
|
onBlur={onTabBlur(ACTIVATED_MAIN)}
|
|
onMouseLeave={onTabBlur(ACTIVATED_MAIN)}
|
|
>
|
|
<h1 className={classNames(css.logo, mainExpanded && css.expanded)}>
|
|
<img
|
|
src={mainExpanded ? shoptimeFullIcon : shopTimeIcon}
|
|
alt=""
|
|
/>
|
|
</h1>
|
|
|
|
{tabActivated &&
|
|
tabs.map((item, index) => (
|
|
<TabItem
|
|
key={"tabitemExpanded" + index}
|
|
onClick={handleNavigation}
|
|
deActivateTab={deActivateTab}
|
|
icons={item.icons}
|
|
expanded={mainExpanded}
|
|
selected={mainSelectedIndex === index}
|
|
title={item.title}
|
|
index={index}
|
|
/>
|
|
))}
|
|
</MainContainer>
|
|
{/* Sub */}
|
|
{
|
|
<Container
|
|
spotlightId="activatedSub"
|
|
className={classNames(
|
|
css.tabWrap,
|
|
css.secondDepthLayout,
|
|
!showSubTab && css.hide
|
|
)}
|
|
onFocus={onTabHasFocus(ACTIVATED_SUB)}
|
|
onMouseOver={onTabHasFocus(ACTIVATED_SUB)}
|
|
onBlur={onTabBlur(ACTIVATED_SUB)}
|
|
onMouseLeave={onTabBlur(ACTIVATED_SUB)}
|
|
>
|
|
{showSubTab &&
|
|
tabs[mainSelectedIndex].children.map((item, index) => {
|
|
return (
|
|
<TabItem
|
|
key={"tabitemSubmenu" + index}
|
|
onClick={onClickSubItem}
|
|
expanded={true}
|
|
index={index}
|
|
isSubItem={true}
|
|
mainExpanded={mainExpanded}
|
|
deActivateTab={deActivateTab}
|
|
title={item.title}
|
|
/>
|
|
);
|
|
})}
|
|
</Container>
|
|
}
|
|
{/* Extra Area*/}
|
|
{tabActivated && (
|
|
<Container
|
|
className={classNames(css.tabWrap, css.extraArea)}
|
|
onClick={onClickExtraArea}
|
|
onFocus={onTabHasFocus(EXTRA_AREA)}
|
|
onMouseOver={onTabHasFocus(EXTRA_AREA)}
|
|
onBlur={onTabBlur(EXTRA_AREA)}
|
|
onMouseLeave={onTabBlur(EXTRA_AREA)}
|
|
/>
|
|
)}
|
|
</CancelableDiv>
|
|
}
|
|
</div>
|
|
);
|
|
}
|