Files
btpxpress-frontend/layout/layout.tsx

148 lines
5.8 KiB
TypeScript
Executable File

'use client';
import React, { useCallback, useEffect, useRef, useContext } from 'react';
import { classNames, DomHandler } from 'primereact/utils';
import { usePathname, useSearchParams } from 'next/navigation';
import { LayoutContext } from './context/layoutcontext';
import { useEventListener, useMountEffect, useResizeListener, useUnmountEffect } from 'primereact/hooks';
import AppTopbar from './AppTopbar';
import AppConfig from './AppConfig';
import AppBreadCrumb from './AppBreadCrumb';
import { PrimeReactContext } from 'primereact/api';
import { Tooltip } from 'primereact/tooltip';
import { ChildContainerProps } from '@/types';
import { Toast } from 'primereact/toast';
import AppProfileMenu from './AppProfileMenu';
import AppSidebar from './AppSidebar';
import GlobalErrorHandler from '../components/GlobalErrorHandler';
const Layout = (props: ChildContainerProps) => {
const { layoutConfig, layoutState, setLayoutState, isSlim, isSlimPlus, isHorizontal, isDesktop } = useContext(LayoutContext);
const { setRipple } = useContext(PrimeReactContext);
const topbarRef = useRef(null);
const sidebarRef = useRef(null);
const copyTooltipRef = useRef(null);
const pathname = usePathname();
const searchParams = useSearchParams();
const [bindMenuOutsideClickListener, unbindMenuOutsideClickListener] = useEventListener({
type: 'click',
listener: (event) => {
const isOutsideClicked = !(sidebarRef.current.isSameNode(event.target) || sidebarRef.current.contains(event.target) || topbarRef.current.menubutton.isSameNode(event.target) || topbarRef.current.menubutton.contains(event.target));
if (isOutsideClicked) {
hideMenu();
}
}
});
const [bindDocumentResizeListener, unbindDocumentResizeListener] = useResizeListener({
listener: () => {
if (isDesktop() && !DomHandler.isTouchDevice()) {
hideMenu();
}
}
});
const hideMenu = useCallback(() => {
setLayoutState((prevLayoutState) => ({
...prevLayoutState,
overlayMenuActive: false,
overlaySubmenuActive: false,
staticMenuMobileActive: false,
menuHoverActive: false,
menuClick: false,
resetMenu: (isSlim() || isSlimPlus() || isHorizontal()) && isDesktop()
}));
}, [isSlim, isHorizontal, isDesktop, setLayoutState]);
const blockBodyScroll = () => {
if (document.body.classList) {
document.body.classList.add('blocked-scroll');
} else {
document.body.className += ' blocked-scroll';
}
};
const unblockBodyScroll = () => {
if (document.body.classList) {
document.body.classList.remove('blocked-scroll');
} else {
document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
};
useMountEffect(() => {
setRipple(layoutConfig.ripple);
});
useEffect(() => {
if (layoutState.overlayMenuActive || layoutState.staticMenuMobileActive || layoutState.overlaySubmenuActive) {
bindMenuOutsideClickListener();
}
if (layoutState.staticMenuMobileActive) {
blockBodyScroll();
(isSlim() || isSlimPlus() || isHorizontal()) && bindDocumentResizeListener();
}
return () => {
unbindMenuOutsideClickListener();
unbindDocumentResizeListener();
unblockBodyScroll();
};
}, [layoutState.overlayMenuActive, layoutState.staticMenuMobileActive, layoutState.overlaySubmenuActive]);
useEffect(() => {
const onRouteChange = () => {
hideMenu();
};
onRouteChange();
}, [pathname, searchParams]);
useUnmountEffect(() => {
unbindMenuOutsideClickListener();
});
const containerClassName = classNames('layout-wrapper', {
'layout-light': layoutConfig.colorScheme === 'light',
'layout-dark': layoutConfig.colorScheme === 'dark',
'layout-overlay': layoutConfig.menuMode === 'overlay',
'layout-static': layoutConfig.menuMode === 'static',
'layout-slim': layoutConfig.menuMode === 'slim',
'layout-slim-plus': layoutConfig.menuMode === 'slim-plus',
'layout-horizontal': layoutConfig.menuMode === 'horizontal',
'layout-reveal': layoutConfig.menuMode === 'reveal',
'layout-drawer': layoutConfig.menuMode === 'drawer',
'layout-static-inactive': layoutState.staticMenuDesktopInactive && layoutConfig.menuMode === 'static',
'layout-overlay-active': layoutState.overlayMenuActive,
'layout-mobile-active': layoutState.staticMenuMobileActive,
'p-ripple-disabled': !layoutConfig.ripple,
'layout-sidebar-active': layoutState.sidebarActive,
'layout-sidebar-anchored': layoutState.anchored
});
return (
<GlobalErrorHandler>
<div className={classNames('layout-container', containerClassName)} data-theme={layoutConfig.colorScheme}>
<Tooltip ref={copyTooltipRef} target=".block-action-copy" position="bottom" content="Copied to clipboard" event="focus" />
<AppSidebar sidebarRef={sidebarRef} />
<div className="layout-content-wrapper">
<AppTopbar ref={topbarRef} sidebarRef={sidebarRef} />
<div className="content-breadcrumb">
<AppBreadCrumb />
</div>
<div className="layout-content">{props.children}</div>
<div className="layout-mask"></div>
</div>
<AppProfileMenu />
<AppConfig />
<Toast></Toast>
</div>
</GlobalErrorHandler>
);
};
export default Layout;