Separate Client Portal & Dashboard
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { ReactElement, forwardRef } from 'react';
|
||||
import { NavLink as RouterLink } from 'react-router-dom';
|
||||
// @mui
|
||||
import { Box, Link } from '@mui/material';
|
||||
// config
|
||||
import { ICON } from '../../../config';
|
||||
// type
|
||||
import { NavItemProps } from '../type';
|
||||
//
|
||||
import Iconify from '../../Iconify';
|
||||
import { ListItemStyle } from './style';
|
||||
import { isExternalLink } from '..';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const NavItemRoot = forwardRef<HTMLButtonElement & HTMLAnchorElement, NavItemProps>(
|
||||
({ item, active, open, onMouseEnter, onMouseLeave }, ref) => {
|
||||
const { title, path, icon, children } = item;
|
||||
|
||||
if (children) {
|
||||
return (
|
||||
<ListItemStyle
|
||||
ref={ref}
|
||||
open={open}
|
||||
activeRoot={active}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<NavItemContent icon={icon} title={title} children={children} />
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
return isExternalLink(path) ? (
|
||||
<ListItemStyle component={Link} href={path} target="_blank" rel="noopener">
|
||||
<NavItemContent icon={icon} title={title} children={children} />
|
||||
</ListItemStyle>
|
||||
) : (
|
||||
<ListItemStyle component={RouterLink} to={path} activeRoot={active}>
|
||||
<NavItemContent icon={icon} title={title} children={children} />
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const NavItemSub = forwardRef<HTMLButtonElement & HTMLAnchorElement, NavItemProps>(
|
||||
({ item, active, open, onMouseEnter, onMouseLeave }, ref) => {
|
||||
const { title, path, icon, children } = item;
|
||||
|
||||
if (children) {
|
||||
return (
|
||||
<ListItemStyle
|
||||
ref={ref}
|
||||
subItem
|
||||
disableRipple
|
||||
open={open}
|
||||
activeSub={active}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<NavItemContent icon={icon} title={title} children={children} subItem />
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
return isExternalLink(path) ? (
|
||||
<ListItemStyle
|
||||
subItem
|
||||
href={path}
|
||||
disableRipple
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
component={Link}
|
||||
>
|
||||
<NavItemContent icon={icon} title={title} children={children} subItem />
|
||||
</ListItemStyle>
|
||||
) : (
|
||||
<ListItemStyle disableRipple component={RouterLink} to={path} activeSub={active} subItem>
|
||||
<NavItemContent icon={icon} title={title} children={children} subItem />
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavItemContentProps = {
|
||||
title: string;
|
||||
icon?: ReactElement;
|
||||
children?: { title: string; path: string }[];
|
||||
subItem?: boolean;
|
||||
};
|
||||
|
||||
function NavItemContent({ icon, title, children, subItem }: NavItemContentProps) {
|
||||
return (
|
||||
<>
|
||||
{icon && (
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
mr: 1,
|
||||
width: ICON.NAVBAR_ITEM_HORIZONTAL,
|
||||
height: ICON.NAVBAR_ITEM_HORIZONTAL,
|
||||
'& svg': { width: '100%', height: '100%' },
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</Box>
|
||||
)}
|
||||
{title}
|
||||
{children && (
|
||||
<Iconify
|
||||
icon={subItem ? 'eva:chevron-right-fill' : 'eva:chevron-down-fill'}
|
||||
sx={{
|
||||
ml: 0.5,
|
||||
width: ICON.NAVBAR_ITEM_HORIZONTAL,
|
||||
height: ICON.NAVBAR_ITEM_HORIZONTAL,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
// type
|
||||
import { NavListProps } from '../type';
|
||||
//
|
||||
import { NavItemRoot, NavItemSub } from './NavItem';
|
||||
import { PaperStyle } from './style';
|
||||
import { getActive } from '..';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavListRootProps = {
|
||||
list: NavListProps;
|
||||
};
|
||||
|
||||
export function NavListRoot({ list }: NavListRootProps) {
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const active = getActive(list.path, pathname);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const hasChildren = list.children;
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
handleClose();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pathname]);
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<>
|
||||
<NavItemRoot
|
||||
open={open}
|
||||
item={list}
|
||||
active={active}
|
||||
ref={menuRef}
|
||||
onMouseEnter={handleOpen}
|
||||
onMouseLeave={handleClose}
|
||||
/>
|
||||
|
||||
<PaperStyle
|
||||
open={open}
|
||||
anchorEl={menuRef.current}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
PaperProps={{
|
||||
onMouseEnter: handleOpen,
|
||||
onMouseLeave: handleClose,
|
||||
}}
|
||||
>
|
||||
{(list.children || []).map((item) => (
|
||||
<NavListSub key={item.title} list={item} />
|
||||
))}
|
||||
</PaperStyle>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <NavItemRoot item={list} active={active} />;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavListSubProps = {
|
||||
list: NavListProps;
|
||||
};
|
||||
|
||||
function NavListSub({ list }: NavListSubProps) {
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const active = getActive(list.path, pathname);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const hasChildren = list.children;
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<>
|
||||
<NavItemSub
|
||||
ref={menuRef}
|
||||
open={open}
|
||||
item={list}
|
||||
active={active}
|
||||
onMouseEnter={handleOpen}
|
||||
onMouseLeave={handleClose}
|
||||
/>
|
||||
|
||||
<PaperStyle
|
||||
open={open}
|
||||
anchorEl={menuRef.current}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
PaperProps={{
|
||||
onMouseEnter: handleOpen,
|
||||
onMouseLeave: handleClose,
|
||||
}}
|
||||
>
|
||||
{(list.children || []).map((item) => (
|
||||
<NavListSub key={item.title} list={item} />
|
||||
))}
|
||||
</PaperStyle>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <NavItemSub item={list} active={active} />;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { memo } from 'react';
|
||||
// @mui
|
||||
import { Stack } from '@mui/material';
|
||||
// type
|
||||
import { NavSectionProps } from '../type';
|
||||
//
|
||||
import { NavListRoot } from './NavList';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const hideScrollbar = {
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none',
|
||||
overflowY: 'scroll',
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
} as const;
|
||||
|
||||
function NavSectionHorizontal({ navConfig }: NavSectionProps) {
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
sx={{ bgcolor: 'background.neutral', borderRadius: 1, px: 0.5 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ ...hideScrollbar, py: 1 }}>
|
||||
{navConfig.map((group) => (
|
||||
<Stack key={group.subheader} direction="row" flexShrink={0}>
|
||||
{group.items.map((list) => (
|
||||
<NavListRoot key={list.title} list={list} />
|
||||
))}
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(NavSectionHorizontal);
|
||||
@@ -0,0 +1,85 @@
|
||||
import { ReactNode } from 'react';
|
||||
// @mui
|
||||
import { alpha, styled } from '@mui/material/styles';
|
||||
import { Button, Popover, ButtonProps, LinkProps } from '@mui/material';
|
||||
// config
|
||||
import { NAVBAR } from '../../../config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type IProps = LinkProps & ButtonProps;
|
||||
|
||||
interface ListItemStyleProps extends IProps {
|
||||
component?: ReactNode;
|
||||
to?: string;
|
||||
activeRoot?: boolean;
|
||||
activeSub?: boolean;
|
||||
subItem?: boolean;
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
export const ListItemStyle = styled(Button, {
|
||||
shouldForwardProp: (prop) =>
|
||||
prop !== 'activeRoot' && prop !== 'activeSub' && prop !== 'subItem' && prop !== 'open',
|
||||
})<ListItemStyleProps>(({ activeRoot, activeSub, subItem, open, theme }) => {
|
||||
const isLight = theme.palette.mode === 'light';
|
||||
|
||||
const activeRootStyle = {
|
||||
color: theme.palette.grey[800],
|
||||
backgroundColor: theme.palette.common.white,
|
||||
boxShadow: `-2px 4px 6px 0 ${alpha(
|
||||
isLight ? theme.palette.grey[500] : theme.palette.common.black,
|
||||
0.16
|
||||
)}`,
|
||||
};
|
||||
|
||||
return {
|
||||
...theme.typography.body2,
|
||||
margin: theme.spacing(0, 0.5),
|
||||
padding: theme.spacing(0, 1),
|
||||
color: theme.palette.text.secondary,
|
||||
height: NAVBAR.DASHBOARD_ITEM_HORIZONTAL_HEIGHT,
|
||||
'&:hover': {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
// activeRoot
|
||||
...(activeRoot && {
|
||||
...theme.typography.subtitle2,
|
||||
...activeRootStyle,
|
||||
'&:hover': { ...activeRootStyle },
|
||||
}),
|
||||
// activeSub
|
||||
...(activeSub && {
|
||||
...theme.typography.subtitle2,
|
||||
color: theme.palette.text.primary,
|
||||
}),
|
||||
// subItem
|
||||
...(subItem && {
|
||||
width: '100%',
|
||||
margin: 0,
|
||||
paddingRight: 0,
|
||||
paddingLeft: theme.spacing(1),
|
||||
justifyContent: 'space-between',
|
||||
}),
|
||||
// open
|
||||
...(open &&
|
||||
!activeRoot && {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const PaperStyle = styled(Popover)(({ theme }) => ({
|
||||
pointerEvents: 'none',
|
||||
'& .MuiPopover-paper': {
|
||||
width: 160,
|
||||
pointerEvents: 'auto',
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: Number(theme.shape.borderRadius) * 1.5,
|
||||
boxShadow: theme.customShadows.dropdown,
|
||||
},
|
||||
}));
|
||||
14
frontend/dashboard/src/components/nav-section/index.ts
Normal file
14
frontend/dashboard/src/components/nav-section/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { matchPath } from 'react-router-dom';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export { default as NavSectionVertical } from './vertical';
|
||||
export { default as NavSectionHorizontal } from './horizontal';
|
||||
|
||||
export function isExternalLink(path: string) {
|
||||
return path.includes('http');
|
||||
}
|
||||
|
||||
export function getActive(path: string, pathname: string) {
|
||||
return path ? !!matchPath({ path: path, end: false }, pathname) : false;
|
||||
}
|
||||
34
frontend/dashboard/src/components/nav-section/type.ts
Normal file
34
frontend/dashboard/src/components/nav-section/type.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ReactElement } from 'react';
|
||||
import { BoxProps } from '@mui/material';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type NavListProps = {
|
||||
title: string;
|
||||
path: string;
|
||||
icon?: ReactElement;
|
||||
info?: ReactElement;
|
||||
children?: {
|
||||
title: string;
|
||||
path: string;
|
||||
children?: { title: string; path: string }[];
|
||||
}[];
|
||||
};
|
||||
|
||||
export type NavItemProps = {
|
||||
item: NavListProps;
|
||||
isCollapse?: boolean;
|
||||
active?: boolean | undefined;
|
||||
open?: boolean;
|
||||
onOpen?: VoidFunction;
|
||||
onMouseEnter?: VoidFunction;
|
||||
onMouseLeave?: VoidFunction;
|
||||
};
|
||||
|
||||
export interface NavSectionProps extends BoxProps {
|
||||
isCollapse?: boolean;
|
||||
navConfig: {
|
||||
subheader: string;
|
||||
items: NavListProps[];
|
||||
}[];
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { NavLink as RouterLink } from 'react-router-dom';
|
||||
// @mui
|
||||
import { Box, Link, ListItemText } from '@mui/material';
|
||||
// type
|
||||
import { NavItemProps } from '../type';
|
||||
//
|
||||
import Iconify from '../../Iconify';
|
||||
import { ListItemStyle, ListItemTextStyle, ListItemIconStyle } from './style';
|
||||
import { isExternalLink } from '..';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function NavItemRoot({ item, isCollapse, open = false, active, onOpen }: NavItemProps) {
|
||||
const { title, path, icon, info, children } = item;
|
||||
|
||||
const renderContent = (
|
||||
<>
|
||||
{icon && <ListItemIconStyle>{icon}</ListItemIconStyle>}
|
||||
<ListItemTextStyle disableTypography primary={title} isCollapse={isCollapse} />
|
||||
{!isCollapse && (
|
||||
<>
|
||||
{info && info}
|
||||
{children && <ArrowIcon open={open} />}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (children) {
|
||||
return (
|
||||
<ListItemStyle onClick={onOpen} activeRoot={active}>
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
return isExternalLink(path) ? (
|
||||
<ListItemStyle component={Link} href={path} target="_blank" rel="noopener">
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
) : (
|
||||
<ListItemStyle component={RouterLink} to={path} activeRoot={active}>
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavItemSubProps = Omit<NavItemProps, 'isCollapse'>;
|
||||
|
||||
export function NavItemSub({ item, open = false, active = false, onOpen }: NavItemSubProps) {
|
||||
const { title, path, info, children } = item;
|
||||
|
||||
const renderContent = (
|
||||
<>
|
||||
<DotIcon active={active} />
|
||||
<ListItemText disableTypography primary={title} />
|
||||
{info && info}
|
||||
{children && <ArrowIcon open={open} />}
|
||||
</>
|
||||
);
|
||||
|
||||
if (children) {
|
||||
return (
|
||||
<ListItemStyle onClick={onOpen} activeSub={active} subItem>
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
return isExternalLink(path) ? (
|
||||
<ListItemStyle component={Link} href={path} target="_blank" rel="noopener" subItem>
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
) : (
|
||||
<ListItemStyle component={RouterLink} to={path} activeSub={active} subItem>
|
||||
{renderContent}
|
||||
</ListItemStyle>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DotIconProps = {
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
export function DotIcon({ active }: DotIconProps) {
|
||||
return (
|
||||
<ListItemIconStyle>
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
width: 4,
|
||||
height: 4,
|
||||
borderRadius: '50%',
|
||||
bgcolor: 'text.disabled',
|
||||
transition: (theme) =>
|
||||
theme.transitions.create('transform', {
|
||||
duration: theme.transitions.duration.shorter,
|
||||
}),
|
||||
...(active && {
|
||||
transform: 'scale(2)',
|
||||
bgcolor: 'primary.main',
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</ListItemIconStyle>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type ArrowIconProps = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
export function ArrowIcon({ open }: ArrowIconProps) {
|
||||
return (
|
||||
<Iconify
|
||||
icon={open ? 'eva:arrow-ios-downward-fill' : 'eva:arrow-ios-forward-fill'}
|
||||
sx={{ width: 16, height: 16, ml: 1 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
// @mui
|
||||
import { List, Collapse } from '@mui/material';
|
||||
// type
|
||||
import { NavListProps } from '../type';
|
||||
//
|
||||
import { NavItemRoot, NavItemSub } from './NavItem';
|
||||
import { getActive } from '..';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavListRootProps = {
|
||||
list: NavListProps;
|
||||
isCollapse: boolean;
|
||||
};
|
||||
|
||||
export function NavListRoot({ list, isCollapse }: NavListRootProps) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const active = getActive(list.path, pathname);
|
||||
|
||||
const [open, setOpen] = useState(active);
|
||||
|
||||
const hasChildren = list.children;
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<>
|
||||
<NavItemRoot
|
||||
item={list}
|
||||
isCollapse={isCollapse}
|
||||
active={active}
|
||||
open={open}
|
||||
onOpen={() => setOpen(!open)}
|
||||
/>
|
||||
|
||||
{!isCollapse && (
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding>
|
||||
{(list.children || []).map((item) => (
|
||||
<NavListSub key={item.title} list={item} />
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <NavItemRoot item={list} active={active} isCollapse={isCollapse} />;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NavListSubProps = {
|
||||
list: NavListProps;
|
||||
};
|
||||
|
||||
function NavListSub({ list }: NavListSubProps) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const active = getActive(list.path, pathname);
|
||||
|
||||
const [open, setOpen] = useState(active);
|
||||
|
||||
const hasChildren = list.children;
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<>
|
||||
<NavItemSub item={list} onOpen={() => setOpen(!open)} open={open} active={active} />
|
||||
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding sx={{ pl: 3 }}>
|
||||
{(list.children || []).map((item) => (
|
||||
<NavItemSub key={item.title} item={item} active={getActive(item.path, pathname)} />
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <NavItemSub item={list} active={active} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { List, Box, ListSubheader } from '@mui/material';
|
||||
// type
|
||||
import { NavSectionProps } from '../type';
|
||||
//
|
||||
import { NavListRoot } from './NavList';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const ListSubheaderStyle = styled((props) => (
|
||||
<ListSubheader disableSticky disableGutters {...props} />
|
||||
))(({ theme }) => ({
|
||||
...theme.typography.overline,
|
||||
paddingTop: theme.spacing(3),
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(1),
|
||||
color: theme.palette.text.primary,
|
||||
transition: theme.transitions.create('opacity', {
|
||||
duration: theme.transitions.duration.shorter,
|
||||
}),
|
||||
}));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function NavSectionVertical({
|
||||
navConfig,
|
||||
isCollapse = false,
|
||||
...other
|
||||
}: NavSectionProps) {
|
||||
return (
|
||||
<Box {...other}>
|
||||
{navConfig.map((group) => (
|
||||
<List key={group.subheader} disablePadding sx={{ px: 2 }}>
|
||||
<ListSubheaderStyle
|
||||
sx={{
|
||||
...(isCollapse && {
|
||||
opacity: 0,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{group.subheader}
|
||||
</ListSubheaderStyle>
|
||||
|
||||
{group.items.map((list) => (
|
||||
<NavListRoot key={list.title} list={list} isCollapse={isCollapse} />
|
||||
))}
|
||||
</List>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { ReactNode } from 'react';
|
||||
// @mui
|
||||
import { alpha, styled } from '@mui/material/styles';
|
||||
import {
|
||||
LinkProps,
|
||||
ListItemText,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemButtonProps,
|
||||
} from '@mui/material';
|
||||
// config
|
||||
import { ICON, NAVBAR } from '../../../config';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type IProps = LinkProps & ListItemButtonProps;
|
||||
|
||||
interface ListItemStyleProps extends IProps {
|
||||
component?: ReactNode;
|
||||
to?: string;
|
||||
activeRoot?: boolean;
|
||||
activeSub?: boolean;
|
||||
subItem?: boolean;
|
||||
}
|
||||
|
||||
export const ListItemStyle = styled(ListItemButton, {
|
||||
shouldForwardProp: (prop) => prop !== 'activeRoot' && prop !== 'activeSub' && prop !== 'subItem',
|
||||
})<ListItemStyleProps>(({ activeRoot, activeSub, subItem, theme }) => ({
|
||||
...theme.typography.body2,
|
||||
position: 'relative',
|
||||
height: NAVBAR.DASHBOARD_ITEM_ROOT_HEIGHT,
|
||||
textTransform: 'capitalize',
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(1.5),
|
||||
marginBottom: theme.spacing(0.5),
|
||||
color: theme.palette.text.secondary,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
// activeRoot
|
||||
...(activeRoot && {
|
||||
...theme.typography.subtitle2,
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
|
||||
}),
|
||||
// activeSub
|
||||
...(activeSub && {
|
||||
...theme.typography.subtitle2,
|
||||
color: theme.palette.text.primary,
|
||||
}),
|
||||
// subItem
|
||||
...(subItem && {
|
||||
height: NAVBAR.DASHBOARD_ITEM_SUB_HEIGHT,
|
||||
}),
|
||||
}));
|
||||
|
||||
interface ListItemTextStyleProps extends ListItemButtonProps {
|
||||
isCollapse?: boolean;
|
||||
}
|
||||
|
||||
export const ListItemTextStyle = styled(ListItemText, {
|
||||
shouldForwardProp: (prop) => prop !== 'isCollapse',
|
||||
})<ListItemTextStyleProps>(({ isCollapse, theme }) => ({
|
||||
whiteSpace: 'nowrap',
|
||||
transition: theme.transitions.create(['width', 'opacity'], {
|
||||
duration: theme.transitions.duration.shorter,
|
||||
}),
|
||||
...(isCollapse && {
|
||||
width: 0,
|
||||
opacity: 0,
|
||||
}),
|
||||
}));
|
||||
|
||||
export const ListItemIconStyle = styled(ListItemIcon)({
|
||||
width: ICON.NAVBAR_ITEM,
|
||||
height: ICON.NAVBAR_ITEM,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'& svg': { width: '100%', height: '100%' },
|
||||
});
|
||||
Reference in New Issue
Block a user