react-garden
Version:
React + TypeScript + ThreeJS app using Material UI on NextJS, Apollo Client, GraphQL + WordPress REST APIs, for ThreeD web development.. a part of the threed.ai code family.
323 lines (298 loc) • 11.1 kB
JavaScript
// ** React Imports
import { useEffect, Fragment } from 'react'
// ** Next Import
import { useRouter } from 'next/router'
// ** MUI Imports
import Chip from '@mui/material/Chip'
import Collapse from '@mui/material/Collapse'
import ListItem from '@mui/material/ListItem'
import Typography from '@mui/material/Typography'
import Box from '@mui/material/Box'
import ListItemIcon from '@mui/material/ListItemIcon'
import { styled, useTheme } from '@mui/material/styles'
import ListItemButton from '@mui/material/ListItemButton'
// ** Third Party Imports
import clsx from 'clsx'
// ** Icons Imports
import ChevronLeft from 'mdi-material-ui/ChevronLeft'
import ChevronRight from 'mdi-material-ui/ChevronRight'
// ** Configs Import
import themeConfig from '~/configs/themeConfig'
// ** Utils
import { hasActiveChild, removeChildren } from '~/@core/layouts/utils'
// ** Custom Components Imports
import VerticalNavItems from './VerticalNavItems'
import UserIcon from '~/layouts/components/UserIcon'
import Translations from '~/layouts/components/Translations'
import CanViewNavGroup from '~/layouts/components/acl/CanViewNavGroup'
const MenuItemTextWrapper = styled(Box)(() => ({
width: '100%',
display: 'flex',
justifyContent: 'space-between',
transition: 'opacity .25s ease-in-out',
...(themeConfig.menuTextTruncate && { overflow: 'hidden' })
}))
const MenuGroupToggleRightIcon = styled(ChevronRight)(({ theme }) => ({
color: theme.palette.text.primary,
transition: 'transform .25s ease-in-out'
}))
const MenuGroupToggleLeftIcon = styled(ChevronLeft)(({ theme }) => ({
color: theme.palette.text.primary,
transition: 'transform .25s ease-in-out'
}))
const VerticalNavGroup = props => {
// ** Props
const {
item,
parent,
settings,
navHover,
navVisible,
isSubToSub,
groupActive,
setGroupActive,
collapsedNavWidth,
currentActiveGroup,
setCurrentActiveGroup,
navigationBorderWidth
} = props
// ** Hooks & Vars
const theme = useTheme()
const router = useRouter()
const currentURL = router.pathname
const { skin, direction, navCollapsed, verticalNavToggleType } = settings
// ** Accordion menu group open toggle
const toggleActiveGroup = (item, parent) => {
let openGroup = groupActive
// ** If Group is already open and clicked, close the group
if (openGroup.includes(item.title)) {
openGroup.splice(openGroup.indexOf(item.title), 1)
// If clicked Group has open group children, Also remove those children to close those groups
if (item.children) {
removeChildren(item.children, openGroup, currentActiveGroup)
}
} else if (parent) {
// ** If Group clicked is the child of an open group, first remove all the open groups under that parent
if (parent.children) {
removeChildren(parent.children, openGroup, currentActiveGroup)
}
// ** After removing all the open groups under that parent, add the clicked group to open group array
if (!openGroup.includes(item.title)) {
openGroup.push(item.title)
}
} else {
// ** If clicked on another group that is not active or open, create openGroup array from scratch
// ** Empty Open Group array
openGroup = []
// ** push Current Active Group To Open Group array
if (currentActiveGroup.every(elem => groupActive.includes(elem))) {
openGroup.push(...currentActiveGroup)
}
// ** Push current clicked group item to Open Group array
if (!openGroup.includes(item.title)) {
openGroup.push(item.title)
}
}
setGroupActive([...openGroup])
}
// ** Menu Group Click
const handleGroupClick = () => {
const openGroup = groupActive
if (verticalNavToggleType === 'collapse') {
if (openGroup.includes(item.title)) {
openGroup.splice(openGroup.indexOf(item.title), 1)
} else {
openGroup.push(item.title)
}
setGroupActive([...openGroup])
} else {
toggleActiveGroup(item, parent)
}
}
useEffect(() => {
if (hasActiveChild(item, currentURL)) {
if (!groupActive.includes(item.title)) groupActive.push(item.title)
} else {
const index = groupActive.indexOf(item.title)
if (index > -1) groupActive.splice(index, 1)
}
setGroupActive([...groupActive])
setCurrentActiveGroup([...groupActive])
// Empty Active Group When Menu is collapsed and not hovered, to fix issue route change
if (navCollapsed && !navHover) {
setGroupActive([])
}
}, [router.asPath])
useEffect(() => {
if (navCollapsed && !navHover) {
setGroupActive([])
}
if ((navCollapsed && navHover) || (groupActive.length === 0 && !navCollapsed)) {
setGroupActive([...currentActiveGroup])
}
}, [navCollapsed, navHover])
useEffect(() => {
if (groupActive.length === 0 && !navCollapsed) {
setGroupActive([])
}
}, [navHover])
const IconTag = parent && !item.icon ? themeConfig.navSubItemIcon : item.icon
const menuGroupCollapsedStyles = navCollapsed && !navHover ? { opacity: 0 } : { opacity: 1 }
const conditionalColor = () => {
if (skin === 'semi-dark' && theme.palette.mode === 'light') {
return {
color: `rgba(${theme.palette.customColors.dark}, 0.68) !important`
}
} else if (skin === 'semi-dark' && theme.palette.mode === 'dark') {
return {
color: `rgba(${theme.palette.customColors.light}, 0.68) !important`
}
} else {
return {
color: `${theme.palette.text.secondary} !important`
}
}
}
const conditionalBgColor = () => {
if (skin === 'semi-dark' && theme.palette.mode === 'light') {
return {
color: `rgba(${theme.palette.customColors.dark}, 0.87)`,
'&:hover': {
backgroundColor: `rgba(${theme.palette.customColors.dark}, 0.04)`
},
'&.Mui-selected': {
backgroundColor: `rgba(${theme.palette.customColors.dark}, 0.08)`,
'&:hover': {
backgroundColor: `rgba(${theme.palette.customColors.dark}, 0.12)`
}
}
}
} else if (skin === 'semi-dark' && theme.palette.mode === 'dark') {
return {
color: `rgba(${theme.palette.customColors.light}, 0.87)`,
'&:hover': {
backgroundColor: `rgba(${theme.palette.customColors.light}, 0.04)`
},
'&.Mui-selected': {
backgroundColor: `rgba(${theme.palette.customColors.light}, 0.08)`,
'&:hover': {
backgroundColor: `rgba(${theme.palette.customColors.light}, 0.12)`
}
}
}
} else {
return {
'&.Mui-selected': {
backgroundColor: theme.palette.action.hover,
'&:hover': {
backgroundColor: theme.palette.action.hover
}
}
}
}
}
return (
<CanViewNavGroup navGroup={item}>
<>
<ListItem
disablePadding
className='nav-group'
onClick={handleGroupClick}
sx={{ mt: 1.5, px: '0 !important', flexDirection: 'column' }}
>
<ListItemButton
className={clsx({
'Mui-selected': groupActive.includes(item.title) || currentActiveGroup.includes(item.title)
})}
sx={{
py: 2.25,
width: '100%',
...conditionalBgColor(),
borderTopRightRadius: 100,
borderBottomRightRadius: 100,
transition: 'padding-left .25s ease-in-out',
pl: navCollapsed && !navHover ? (collapsedNavWidth - navigationBorderWidth - 24) / 8 : 5.5,
pr: navCollapsed && !navHover ? ((collapsedNavWidth - navigationBorderWidth - 24) / 2 - 5) / 4 : 3.5
}}
>
{isSubToSub ? null : (
<ListItemIcon
sx={{
color: 'text.primary',
transition: 'margin .25s ease-in-out',
...(parent && navCollapsed && !navHover ? {} : { mr: 2.5 }),
...(navCollapsed && !navHover ? { mr: 0 } : {}),
...(parent && item.children ? { ml: 1.25, mr: 3.75 } : {})
}}
>
<UserIcon
icon={IconTag}
componentType='vertical-menu'
iconProps={{ sx: { ...(parent ? { fontSize: '0.875rem' } : {}) } }}
/>
</ListItemIcon>
)}
<MenuItemTextWrapper sx={{ ...menuGroupCollapsedStyles, ...(isSubToSub ? { ml: 9 } : {}) }}>
<Typography
{...((themeConfig.menuTextTruncate || (!themeConfig.menuTextTruncate && navCollapsed && !navHover)) && {
noWrap: true
})}
>
<Translations text={item.title} />
</Typography>
<Box className='menu-item-meta' sx={{ ml: 0.8, display: 'flex', alignItems: 'center' }}>
{item.badgeContent ? (
<Chip
label={item.badgeContent}
color={item.badgeColor || 'primary'}
sx={{
mr: 0.8,
height: 20,
fontWeight: 500,
'& .MuiChip-label': { px: 1.5, textTransform: 'capitalize' }
}}
/>
) : null}
{direction === 'ltr' ? (
<MenuGroupToggleRightIcon
sx={{
...conditionalColor(),
...(groupActive.includes(item.title) ? { transform: 'rotate(90deg)' } : {})
}}
/>
) : (
<MenuGroupToggleLeftIcon
sx={{
...conditionalColor(),
...(groupActive.includes(item.title) ? { transform: 'rotate(-90deg)' } : {})
}}
/>
)}
</Box>
</MenuItemTextWrapper>
</ListItemButton>
<Collapse
component='ul'
onClick={e => e.stopPropagation()}
in={groupActive.includes(item.title)}
sx={{
pl: 0,
width: '100%',
...menuGroupCollapsedStyles,
transition: 'all .25s ease-in-out'
}}
>
<VerticalNavItems
{...props}
parent={item}
navVisible={navVisible}
verticalNavItems={item.children}
isSubToSub={parent && item.children ? item : undefined}
/>
</Collapse>
</ListItem>
</>
</CanViewNavGroup>
)
}
export default VerticalNavGroup