merge
This commit is contained in:
@@ -1,45 +1,457 @@
|
||||
import { useMemo } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { addPanels } from "../../features/panels/panelsSlice";
|
||||
import Spotlight from "@enact/spotlight";
|
||||
import classNames from "classnames";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import TabItem from "./TabItem";
|
||||
import css from "./TabLayout.module.less";
|
||||
//enact
|
||||
import Skinnable from "@enact/sandstone/Skinnable";
|
||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||
import { Cancelable } from "@enact/ui/Cancelable";
|
||||
//아이콘
|
||||
import { Job } from "@enact/core/util";
|
||||
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 shoptimeFullIcon from "../../../assets/icons/ic-lnb-logo-shoptime@3x.png";
|
||||
import shopTimeIcon from "../../../assets/icons/ic-lnb-shoptime-symbol@3x.png";
|
||||
|
||||
import * as Config from "../../utils/Config";
|
||||
import { $L } from "../../utils/helperMethods";
|
||||
const Container = SpotlightContainerDecorator(
|
||||
{ enterTo: "default-element" },
|
||||
"div"
|
||||
);
|
||||
|
||||
export default function TabLayout(props) {
|
||||
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;
|
||||
|
||||
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 titles = useSelector((state) => state.menu.title);
|
||||
// const items = useSelector((state) => state.menu.items);
|
||||
|
||||
const menuItems = useMemo(
|
||||
() => [
|
||||
{ label: $L("MY PAGE"), panel: Config.panel_names.MY_PAGE_PANEL },
|
||||
{ label: $L("CATEGORY"), panel: Config.panel_names.CATEGORY_PANEL },
|
||||
{ label: $L("SEARCH"), panel: Config.panel_names.SEARCH_PANEL },
|
||||
{ label: $L("HOME"), panel: Config.panel_names.HOME_PANEL },
|
||||
{ label: $L("ON SALE"), panel: Config.panel_names.ON_SALE_PANEL },
|
||||
{
|
||||
label: $L("TRENDING NOW"),
|
||||
panel: Config.panel_names.TRENDING_NOW_PANEL,
|
||||
title: titles[1],
|
||||
icons: CategoryIcon,
|
||||
children: [
|
||||
{
|
||||
title: "123123",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ label: $L("HOT PICKS"), panel: Config.panel_names.HOT_PICKS_PANEL },
|
||||
{ label: $L("CART"), panel: Config.panel_names.CART_PANEL },
|
||||
{
|
||||
label: $L("FEATURED BRANDS"),
|
||||
panel: Config.panel_names.FEATURED_BRANDS_PANEL,
|
||||
title: titles[0],
|
||||
icons: MyPageIcon,
|
||||
children: [
|
||||
{
|
||||
title: "123123",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: titles[2],
|
||||
icons: SearchIcon,
|
||||
children: [
|
||||
{
|
||||
title: "12323232",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: titles[3],
|
||||
icons: HomeIcon,
|
||||
children: [
|
||||
{
|
||||
title: "43534543",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: titles[8],
|
||||
icons: FeaturedBrandIcon,
|
||||
children: [
|
||||
{
|
||||
title: "dsadasd",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: titles[4],
|
||||
icons: OnSaleIcon,
|
||||
children: [
|
||||
{
|
||||
title: "",
|
||||
target: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: titles[5],
|
||||
icons: TrendingNowIcon,
|
||||
},
|
||||
{
|
||||
title: titles[6],
|
||||
icons: HotPicksIcon,
|
||||
},
|
||||
{
|
||||
title: titles[7],
|
||||
icons: CartIcon,
|
||||
},
|
||||
|
||||
// 메뉴 추가 필요 20240112 chw
|
||||
],
|
||||
[]
|
||||
[mainExpanded]
|
||||
);
|
||||
|
||||
const handleNavigation = (panel) => {
|
||||
dispatch(addPanels({ name: panel, panelInfo: {} }));
|
||||
};
|
||||
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;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setTabs(makeTabmenu());
|
||||
}, []);
|
||||
|
||||
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: {
|
||||
console.log("#tabs[mainSelectedIndex]", tabs[mainSelectedIndex]);
|
||||
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();
|
||||
} else if (cursorVisible) {
|
||||
setMainExpanded(true);
|
||||
}
|
||||
},
|
||||
[cursorVisible, 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) {
|
||||
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>
|
||||
{menuItems.map((item, index) => (
|
||||
<button key={index} onClick={() => handleNavigation(item.panel)}>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
{/* 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)}
|
||||
>
|
||||
<img
|
||||
className={classNames(css.logo, mainExpanded && css.expanded)}
|
||||
src={mainExpanded ? shoptimeFullIcon : shopTimeIcon}
|
||||
alt=""
|
||||
/>
|
||||
{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}
|
||||
{...item}
|
||||
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>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user