import React, { isValidElement, memo, useCallback, useEffect, useMemo, useState } from 'react'; import type { ReactNode, Dispatch, SetStateAction } from 'react'; import ClickAwayListener from '@mui/base/ClickAwayListener'; import Drawer from '@mui/material/Drawer'; import List from '@mui/material/List'; import Box from '@mui/material/Box'; import { ASSETS_URL } from '../../consts/common'; import noop from 'lodash/noop'; import values from 'lodash/values'; import map from 'lodash/map'; import clsx from 'clsx'; import type { MainNavItem } from '../@navigation/breadcrumbs'; import { DrawerButton } from '../@navigation/drawer-button'; import type { DrawerButtonProps } from '../@navigation/drawer-button'; import { ClientImage } from '../client-image'; import type { ClientImageMenuItemProps } from '../client-image'; import type { DropDownMenuProps } from '../dropdown-menu'; import createClasses from './styles'; import type { ClassesProps } from './styles'; import { CustomIcon } from '../custom-icon'; import { SidebarItem } from './sidebarItems'; import type { DrawerStates } from './sidebarItems'; export interface SidebarProps extends ClassesProps { /** * Logo which will be displayed at the top of the component when open and when `closedLogo` ins't provided. */ logo: ReactNode; /** * Logo which will be displayed at the top of the component when closed. */ closedLogo?: ReactNode; /** * Menu items to display. */ sidebarItemsMap: Record; /** * The open state of the drawer. * @default 'closed' */ open?: DrawerStates; /** * Set the open state of the drawer. */ setOpen?: Dispatch>; /** * Whether to show sidebar button or not. */ showDrawerButton?: boolean; /** * Control the drawer from layout */ handleDrawerChange?: (event?: Event) => void; /** * Provide to override classes. */ classes?: ReturnType; /** * mspClientPicker will be displayed below logo and on top of sidebar elements when the tenant type is MSP. */ mspClientPicker?: { isMSP?: boolean; props?: { createNewOrg?: () => void; onClientClick?: NonNullable; bottomBtnText?: string; orgList?: ClientImageMenuItemProps[]; noResultsText?: string; selectValue?: string; controlled?: boolean; }; }; /** * If true, the client is running inside CP's infinity portal and header and logo will not be visible. */ hideHeader?: boolean; } const Sidebar = (props: SidebarProps) => { const { logo, closedLogo, sidebarItemsMap, drawerWidthClose, drawerWidthOpen, open = 'closed', setOpen, showDrawerButton = true, classes, mspClientPicker, handleDrawerChange, hideHeader } = props; const MINIMUM_SCREEN_WIDTH_FOR_LOCK = 1366; const styles = createClasses({ drawerWidthClose, drawerWidthOpen, open, hideHeader }); const listItems = useMemo(() => values(sidebarItemsMap), [sidebarItemsMap]); const handleDrawerStateChange = useCallback( (currentState: DrawerStates): DrawerButtonProps['onClick'] => e => { e.stopPropagation(); switch (true) { case currentState === 'locked': setOpen?.('closed'); break; case currentState === 'open' && window.innerWidth >= MINIMUM_SCREEN_WIDTH_FOR_LOCK: setOpen?.('locked'); break; case currentState === 'open': setOpen?.('closed'); break; case currentState === 'closed' && window.innerWidth <= MINIMUM_SCREEN_WIDTH_FOR_LOCK: setOpen?.('open'); break; case currentState === 'closed': setOpen?.('locked'); break; } }, [setOpen] ); const handleMouseEvents = useCallback( (state: 'open' | 'closed' | 'drawer'): React.MouseEventHandler => () => { if (open === 'locked' && handleDrawerChange) { handleDrawerChange(); } if (state === 'drawer') { setOpen?.(open); } else { if (open !== 'locked') { setOpen?.(state); } } }, [handleDrawerChange, open, setOpen] ); const handleClickAway = useCallback(() => { if (open === 'open') { return setOpen?.('closed'); } }, [open, setOpen]); return ( {showDrawerButton && ( )} {isValidElement(logo) && !hideHeader && ( {closedLogo && open === 'closed' ? closedLogo : logo} )} {mspClientPicker?.isMSP && (open === 'open' || open === 'locked') ? ( { mspClientPicker.props?.createNewOrg?.(); open !== 'locked' && setOpen?.('closed'); } } : undefined, searchPlaceholder: 'Search', noResultsText: mspClientPicker.props?.noResultsText, listItems: map(mspClientPicker.props?.orgList, item => { return { ...item, value: item.value || item.name }; }), handleClick: mspClientPicker.props?.onClientClick || (() => noop) }} /> ) : mspClientPicker?.isMSP && open === 'closed' ? ( ) : null} {listItems.map(item => ( ))} ); }; const m = memo(Sidebar); export { m as Sidebar };