This commit is contained in:
고동영
2024-01-24 13:52:43 +09:00
parent 75e00c4485
commit 32c446f1ae
43 changed files with 935 additions and 32 deletions

View File

@@ -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>
}
</>
);
}