new-marketplace
143 строки · 5.2 Кб
1/* eslint-disable react-hooks/exhaustive-deps */
2'use client';
3
4import { useRouter } from 'next/navigation';
5import { useEventListener, useMountEffect, useUnmountEffect } from 'primereact/hooks';
6import React, { useContext, useEffect, useRef } from 'react';
7import { classNames } from 'primereact/utils';
8import AppFooter from './AppFooter';
9import AppSidebar from './AppSidebar';
10import AppTopbar from './AppTopbar';
11import AppConfig from './AppConfig';
12import { LayoutContext } from './context/layoutcontext';
13import { PrimeReactContext } from 'primereact/api';
14import { ChildContainerProps, LayoutState, AppTopbarRef } from '@/types';
15import { usePathname, useSearchParams } from 'next/navigation';
16
17const Layout = ({ children }: ChildContainerProps) => {
18const { layoutConfig, layoutState, setLayoutState } = useContext(LayoutContext);
19const { setRipple } = useContext(PrimeReactContext);
20const topbarRef = useRef<AppTopbarRef>(null);
21const sidebarRef = useRef<HTMLDivElement>(null);
22const [bindMenuOutsideClickListener, unbindMenuOutsideClickListener] = useEventListener({
23type: 'click',
24listener: (event) => {
25const isOutsideClicked = !(
26sidebarRef.current?.isSameNode(event.target as Node) ||
27sidebarRef.current?.contains(event.target as Node) ||
28topbarRef.current?.menubutton?.isSameNode(event.target as Node) ||
29topbarRef.current?.menubutton?.contains(event.target as Node)
30);
31
32if (isOutsideClicked) {
33hideMenu();
34}
35}
36});
37
38const pathname = usePathname();
39const searchParams = useSearchParams();
40useEffect(() => {
41hideMenu();
42hideProfileMenu();
43}, [pathname, searchParams]);
44
45const [bindProfileMenuOutsideClickListener, unbindProfileMenuOutsideClickListener] = useEventListener({
46type: 'click',
47listener: (event) => {
48const isOutsideClicked = !(
49topbarRef.current?.topbarmenu?.isSameNode(event.target as Node) ||
50topbarRef.current?.topbarmenu?.contains(event.target as Node) ||
51topbarRef.current?.topbarmenubutton?.isSameNode(event.target as Node) ||
52topbarRef.current?.topbarmenubutton?.contains(event.target as Node)
53);
54
55if (isOutsideClicked) {
56hideProfileMenu();
57}
58}
59});
60
61const hideMenu = () => {
62setLayoutState((prevLayoutState: LayoutState) => ({
63...prevLayoutState,
64overlayMenuActive: false,
65staticMenuMobileActive: false,
66menuHoverActive: false
67}));
68unbindMenuOutsideClickListener();
69unblockBodyScroll();
70};
71
72const hideProfileMenu = () => {
73setLayoutState((prevLayoutState: LayoutState) => ({
74...prevLayoutState,
75profileSidebarVisible: false
76}));
77unbindProfileMenuOutsideClickListener();
78};
79
80const blockBodyScroll = (): void => {
81if (document.body.classList) {
82document.body.classList.add('blocked-scroll');
83} else {
84document.body.className += ' blocked-scroll';
85}
86};
87
88const unblockBodyScroll = (): void => {
89if (document.body.classList) {
90document.body.classList.remove('blocked-scroll');
91} else {
92document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
93}
94};
95
96useEffect(() => {
97if (layoutState.overlayMenuActive || layoutState.staticMenuMobileActive) {
98bindMenuOutsideClickListener();
99}
100
101layoutState.staticMenuMobileActive && blockBodyScroll();
102}, [layoutState.overlayMenuActive, layoutState.staticMenuMobileActive]);
103
104useEffect(() => {
105if (layoutState.profileSidebarVisible) {
106bindProfileMenuOutsideClickListener();
107}
108}, [layoutState.profileSidebarVisible]);
109
110useUnmountEffect(() => {
111unbindMenuOutsideClickListener();
112unbindProfileMenuOutsideClickListener();
113});
114
115const containerClass = classNames('layout-wrapper', {
116'layout-overlay': layoutConfig.menuMode === 'overlay',
117'layout-static': layoutConfig.menuMode === 'static',
118'layout-static-inactive': layoutState.staticMenuDesktopInactive && layoutConfig.menuMode === 'static',
119'layout-overlay-active': layoutState.overlayMenuActive,
120'layout-mobile-active': layoutState.staticMenuMobileActive,
121'p-input-filled': layoutConfig.inputStyle === 'filled',
122'p-ripple-disabled': !layoutConfig.ripple
123});
124
125return (
126<React.Fragment>
127<div className={containerClass}>
128<AppTopbar ref={topbarRef} />
129<div ref={sidebarRef} className="layout-sidebar">
130<AppSidebar />
131</div>
132<div className="layout-main-container">
133<div className="layout-main">{children}</div>
134<AppFooter />
135</div>
136<AppConfig />
137<div className="layout-mask"></div>
138</div>
139</React.Fragment>
140);
141};
142
143export default Layout;
144