Files
btpxpress-frontend/layout/AppMenuitem.tsx
2025-10-01 01:39:07 +00:00

188 lines
7.0 KiB
TypeScript

'use client';
import Link from 'next/link';
import { usePathname, useSearchParams } from 'next/navigation';
import { Ripple } from 'primereact/ripple';
import { classNames } from 'primereact/utils';
import { useContext, useEffect, useRef } from 'react';
import { LayoutContext } from './context/layoutcontext';
import { MenuContext } from './context/menucontext';
import { useSubmenuOverlayPosition } from './hooks/useSubmenuOverlayPosition';
import { AppMenuItemProps } from '../types/layout';
const AppMenuitem = (props: AppMenuItemProps) => {
const { activeMenu, setActiveMenu } = useContext(MenuContext);
const { isSlim, isSlimPlus, isHorizontal, isDesktop, setLayoutState, layoutState, layoutConfig } = useContext(LayoutContext);
const searchParams = useSearchParams();
const pathname = usePathname();
const submenuRef = useRef(null);
const menuitemRef = useRef(null);
const item = props.item;
const key = props.parentKey ? props.parentKey + '-' + props.index : String(props.index);
const isActiveRoute = item.to && pathname === item.to;
const active = activeMenu === key || !!(activeMenu && activeMenu.startsWith(key + '-'));
useSubmenuOverlayPosition({
target: menuitemRef.current,
overlay: submenuRef.current,
container: menuitemRef.current && menuitemRef.current.closest('.layout-menu-container'),
when: props.root && active && (isSlim() || isSlimPlus() || isHorizontal()) && isDesktop()
});
useEffect(() => {
if (layoutState.resetMenu) {
setActiveMenu('');
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
resetMenu: false
}));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [layoutState]);
useEffect(() => {
if (!(isSlim() || isHorizontal() || isSlimPlus()) && isActiveRoute) {
setActiveMenu(key);
}
}, [layoutConfig]);
useEffect(() => {
const url = pathname + searchParams.toString();
const onRouteChange = (url) => {
if (!(isSlim() || isHorizontal() || isSlimPlus()) && item.to && item.to === url) {
setActiveMenu(key);
}
};
onRouteChange(url);
}, [pathname, searchParams]);
const itemClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
//avoid processing disabled items
if (item.disabled) {
event.preventDefault();
return;
}
// navigate with hover
if (props.root && (isSlim() || isHorizontal() || isSlimPlus())) {
const isSubmenu = event.currentTarget.closest('.layout-root-menuitem.active-menuitem > ul') !== null;
if (isSubmenu)
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
menuHoverActive: true
}));
else
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
menuHoverActive: !prevLayoutState.menuHoverActive
}));
}
//execute command
if (item.command) {
item.command({ originalEvent: event, item: item });
}
// toggle active state
if (item.items) {
setActiveMenu(active ? props.parentKey : key);
if (props.root && !active && (isSlim() || isHorizontal() || isSlimPlus())) {
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
overlaySubmenuActive: true
}));
}
} else {
if (!isDesktop()) {
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
staticMenuMobileActive: !prevLayoutState.staticMenuMobileActive
}));
}
if (isSlim() || isHorizontal() || isSlimPlus()) {
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
menuHoverActive: false
}));
}
setActiveMenu(key);
}
};
const onMouseEnter = () => {
// activate item on hover
if (props.root && (isSlim() || isHorizontal() || isSlimPlus()) && isDesktop()) {
if (!active && layoutState.menuHoverActive) {
setActiveMenu(key);
}
}
};
const subMenu =
item.items && item.visible !== false ? (
<ul ref={submenuRef}>
{item.items.map((child, i) => {
return <AppMenuitem item={child} index={i} className={child.badgeClass} parentKey={key} key={child.label} />;
})}
</ul>
) : null;
return (
<li
ref={menuitemRef}
className={classNames({
'layout-root-menuitem': props.root,
'active-menuitem': active
})}
>
{props.root && item.visible !== false && <div className="layout-menuitem-root-text">{item.label}</div>}
{(!item.to || item.items) && item.visible !== false ? (
<>
<a
href={item.url}
onClick={(e) => itemClick(e)}
className={classNames(item.class, 'p-ripple tooltip-target')}
target={item.target}
data-pr-tooltip={item.label}
data-pr-disabled={!(isSlim() && props.root && !layoutState.menuHoverActive)}
tabIndex={0}
onMouseEnter={onMouseEnter}
>
<i className={classNames('layout-menuitem-icon', item.icon)}></i>
<span className="layout-menuitem-text">{item.label}</span>
{item.items && <i className="pi pi-fw pi-angle-down layout-submenu-toggler"></i>}
<Ripple />
</a>
</>
) : null}
{item.to && !item.items && item.visible !== false ? (
<>
<Link
href={item.to}
replace={item.replaceUrl}
onClick={(e) => itemClick(e)}
className={classNames(item.class, 'p-ripple ', {
'active-route': isActiveRoute
})}
tabIndex={0}
onMouseEnter={onMouseEnter}
>
<i className={classNames('layout-menuitem-icon', item.icon)}></i>
<span className="layout-menuitem-text">{item.label}</span>
{item.items && <i className="pi pi-fw pi-angle-down layout-submenu-toggler"></i>}
<Ripple />
</Link>
</>
) : null}
{subMenu}
</li>
);
};
export default AppMenuitem;