forked from ddrilling/asb_cloud_front
718d32f2b9
* LayoutPortal переделан в обёртку для сокращения повторений * Добавлен контекст и хук для обновления параметров LayoutPortal
129 lines
5.6 KiB
TypeScript
129 lines
5.6 KiB
TypeScript
import { Button, Layout, LayoutProps, Menu, SiderProps } from 'antd'
|
||
import { Key, memo, ReactNode, Suspense, useCallback, useEffect, useMemo, useState } from 'react'
|
||
import { ItemType } from 'antd/lib/menu/hooks/useItems'
|
||
import { Link, Outlet } from 'react-router-dom'
|
||
import {
|
||
ApartmentOutlined,
|
||
CodeOutlined,
|
||
HomeOutlined,
|
||
MenuFoldOutlined,
|
||
MenuUnfoldOutlined,
|
||
UserOutlined,
|
||
} from '@ant-design/icons'
|
||
|
||
import { LayoutPropsContext } from '@asb/context'
|
||
import PageHeader from '@components/PageHeader'
|
||
import { UserMenu, UserMenuProps } from '@components/UserMenu'
|
||
import { WellTreeSelector, WellTreeSelectorProps } from '@components/selectors/WellTreeSelector'
|
||
import { isURLAvailable, wrapPrivateComponent } from '@utils'
|
||
|
||
import SuspenseFallback from './SuspenseFallback'
|
||
|
||
import '@styles/layout.less'
|
||
|
||
const { Content, Sider } = Layout
|
||
|
||
export type LayoutPortalProps = LayoutProps & {
|
||
title?: ReactNode
|
||
sheet?: boolean
|
||
showSelector?: boolean
|
||
selectorProps?: WellTreeSelectorProps
|
||
sider?: boolean | JSX.Element
|
||
siderProps?: SiderProps & { userMenuProps?: UserMenuProps }
|
||
isAdmin?: boolean
|
||
fallback?: JSX.Element
|
||
}
|
||
|
||
const defaultProps: LayoutPortalProps = {
|
||
title: 'Единая цифровая платформа',
|
||
sider: true,
|
||
sheet: true,
|
||
}
|
||
|
||
const makeItem = (title: string, key: Key, icon: JSX.Element, label?: ReactNode, onClick?: () => void) => ({ icon, key, title, label: label ?? title, onClick })
|
||
|
||
const _LayoutPortal = memo(() => {
|
||
const [menuCollapsed, setMenuCollapsed] = useState<boolean>(true)
|
||
const [wellsTreeOpen, setWellsTreeOpen] = useState<boolean>(false)
|
||
const [userMenuOpen, setUserMenuOpen] = useState<boolean>(false)
|
||
const [currentWell, setCurrentWell] = useState<string>('')
|
||
const [props, setProps] = useState<LayoutPortalProps>(defaultProps)
|
||
|
||
const { isAdmin, title, sheet, showSelector, selectorProps, sider, siderProps, fallback, ...other } = useMemo(() => props, [props])
|
||
|
||
const setLayoutProps = useCallback((props: LayoutPortalProps) => setProps({ ...defaultProps, ...props}), [])
|
||
|
||
useEffect(() => {
|
||
if (typeof showSelector === 'boolean')
|
||
setWellsTreeOpen(showSelector)
|
||
}, [showSelector])
|
||
|
||
const menuItems = useMemo(() => [
|
||
!isAdmin && makeItem(currentWell, 'well', <ApartmentOutlined/>, null, () => setWellsTreeOpen((prev) => !prev)),
|
||
isAdmin && makeItem('Вернуться на сайт', 'go_back', <HomeOutlined />, <Link to={'/'}>Домой</Link>),
|
||
!isAdmin && isURLAvailable('/admin') && makeItem('Панель администратора', 'admin_panel', <CodeOutlined />, <Link to={'/admin'}>Панель администратора</Link>),
|
||
makeItem('Профиль', 'profile', <UserOutlined/>, null, () => setUserMenuOpen((prev) => !prev)),
|
||
].filter(Boolean) as ItemType[], [isAdmin, currentWell])
|
||
|
||
return (
|
||
<Layout className={`page-layout ${isAdmin ? 'page-layout-admin' : ''}`}>
|
||
{(sider || siderProps) && (
|
||
<Sider {...siderProps} collapsed={menuCollapsed} trigger={null} collapsible className={`menu-sider ${siderProps?.className || ''}`}>
|
||
<div className={'sider-content'}>
|
||
<button className={'sider-toogle'} onClick={() => setMenuCollapsed((prev) => !prev)}>
|
||
{menuCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||
</button>
|
||
<div className={'scrollable hide-slider'}>
|
||
{sider}
|
||
</div>
|
||
<Menu
|
||
mode={'inline'}
|
||
items={menuItems}
|
||
theme={'dark'}
|
||
selectable={false}
|
||
/>
|
||
<UserMenu
|
||
open={userMenuOpen}
|
||
onClose={() => setUserMenuOpen(false)}
|
||
isAdmin={isAdmin}
|
||
{...siderProps?.userMenuProps}
|
||
/>
|
||
</div>
|
||
</Sider>
|
||
)}
|
||
<Layout className={'page-content'}>
|
||
<PageHeader title={title}>
|
||
{isAdmin ? (
|
||
<Button size={'large'}>
|
||
<Link to={'/'}>Вернуться на сайт</Link>
|
||
</Button>
|
||
) : (
|
||
<>
|
||
<WellTreeSelector
|
||
open={wellsTreeOpen}
|
||
onClose={() => setWellsTreeOpen(false)}
|
||
{...selectorProps}
|
||
onChange={(well) => setCurrentWell(well ?? 'Выберите месторождение')}
|
||
/>
|
||
<Button onClick={() => setWellsTreeOpen((prev) => !prev)}>{currentWell}</Button>
|
||
</>
|
||
)}
|
||
</PageHeader>
|
||
<Content {...other} className={`${sheet ? 'site-layout-background sheet' : ''} ${other.className ?? ''}`}>
|
||
<LayoutPropsContext.Provider value={setLayoutProps}>
|
||
<Suspense fallback={fallback ?? <SuspenseFallback style={{ minHeight: '100%' }} />}>
|
||
<Outlet />
|
||
</Suspense>
|
||
</LayoutPropsContext.Provider>
|
||
</Content>
|
||
</Layout>
|
||
</Layout>
|
||
)
|
||
})
|
||
|
||
export const LayoutPortal = wrapPrivateComponent(_LayoutPortal, {
|
||
requirements: ['Deposit.get'],
|
||
})
|
||
|
||
export default LayoutPortal
|