mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
feat: update configuration menu (#10041)
Updated "Configure" navigation, with all interactions including expanding/collapsing size of the menu.
This commit is contained in:
parent
e67e60a363
commit
016d82a797
@ -0,0 +1,77 @@
|
|||||||
|
import { type FC, useEffect, useState } from 'react';
|
||||||
|
import { MenuListAccordion } from './ListItems.tsx';
|
||||||
|
import { useExpanded } from './useExpanded.ts';
|
||||||
|
import type { NavigationMode } from './NavigationMode.tsx';
|
||||||
|
import { IconRenderer } from './IconRenderer.tsx';
|
||||||
|
import { ConfigurationNavigationList } from './ConfigurationNavigationList.tsx';
|
||||||
|
import { useRoutes } from './useRoutes.ts';
|
||||||
|
|
||||||
|
type ConfigurationAccordionProps = {
|
||||||
|
mode: NavigationMode;
|
||||||
|
setMode;
|
||||||
|
activeItem?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConfigurationAccordion: FC<ConfigurationAccordionProps> = ({
|
||||||
|
mode,
|
||||||
|
setMode,
|
||||||
|
activeItem,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
const [expanded, changeExpanded] = useExpanded<'configure'>();
|
||||||
|
const [temporarilyExpanded, setTemporarilyExpanded] = useState(false);
|
||||||
|
const { routes } = useRoutes();
|
||||||
|
const subRoutes = routes.mainNavRoutes;
|
||||||
|
const hasActiveItem = Boolean(
|
||||||
|
activeItem && subRoutes.some((route) => route.path === activeItem),
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mode === 'mini') {
|
||||||
|
setTemporarilyExpanded(false);
|
||||||
|
}
|
||||||
|
}, [mode]);
|
||||||
|
|
||||||
|
const onExpandChange = () => {
|
||||||
|
changeExpanded('configure', !expanded.includes('configure'));
|
||||||
|
|
||||||
|
if (temporarilyExpanded) {
|
||||||
|
setTemporarilyExpanded(false);
|
||||||
|
setMode('mini');
|
||||||
|
}
|
||||||
|
if (mode === 'mini') {
|
||||||
|
setTemporarilyExpanded(true);
|
||||||
|
setMode('full');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemClick = () => {
|
||||||
|
if (temporarilyExpanded) {
|
||||||
|
setTemporarilyExpanded(false);
|
||||||
|
setMode('mini');
|
||||||
|
}
|
||||||
|
onClick?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuListAccordion
|
||||||
|
title='Configure'
|
||||||
|
expanded={
|
||||||
|
(expanded.includes('configure') || temporarilyExpanded) &&
|
||||||
|
mode === 'full'
|
||||||
|
}
|
||||||
|
onExpandChange={onExpandChange}
|
||||||
|
mode={mode}
|
||||||
|
icon={<IconRenderer path='Configure' />}
|
||||||
|
active={hasActiveItem}
|
||||||
|
>
|
||||||
|
<ConfigurationNavigationList
|
||||||
|
routes={subRoutes}
|
||||||
|
mode={mode}
|
||||||
|
onClick={onItemClick}
|
||||||
|
activeItem={activeItem}
|
||||||
|
/>
|
||||||
|
</MenuListAccordion>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { NavigationMode } from './NavigationMode.tsx';
|
import type { NavigationMode } from './NavigationMode.ts';
|
||||||
import { Typography } from '@mui/material';
|
import { Typography } from '@mui/material';
|
||||||
import Accordion from '@mui/material/Accordion';
|
import Accordion from '@mui/material/Accordion';
|
||||||
import AccordionDetails from '@mui/material/AccordionDetails';
|
import AccordionDetails from '@mui/material/AccordionDetails';
|
||||||
@ -21,7 +21,7 @@ const AccordionHeader: FC<{ children?: React.ReactNode }> = ({ children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SecondaryNavigation: FC<{
|
export const ConfigurationNavigation: FC<{
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
onExpandChange: (expanded: boolean) => void;
|
onExpandChange: (expanded: boolean) => void;
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { INavigationMenuItem } from 'interfaces/route';
|
import type { INavigationMenuItem } from 'interfaces/route';
|
||||||
import type { NavigationMode } from './NavigationMode.tsx';
|
import type { NavigationMode } from './NavigationMode.ts';
|
||||||
import { FullListItem, MiniListItem } from './ListItems.tsx';
|
import { MenuListItem } from './ListItems.tsx';
|
||||||
import { List } from '@mui/material';
|
import { List } from '@mui/material';
|
||||||
import { IconRenderer } from './IconRenderer.tsx';
|
import { IconRenderer } from './IconRenderer.tsx';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
@ -9,20 +9,19 @@ import StopRoundedIcon from '@mui/icons-material/StopRounded';
|
|||||||
import { useShowBadge } from 'component/layout/components/EnterprisePlanBadge/useShowBadge';
|
import { useShowBadge } from 'component/layout/components/EnterprisePlanBadge/useShowBadge';
|
||||||
import { EnterprisePlanBadge } from 'component/layout/components/EnterprisePlanBadge/EnterprisePlanBadge';
|
import { EnterprisePlanBadge } from 'component/layout/components/EnterprisePlanBadge/EnterprisePlanBadge';
|
||||||
|
|
||||||
export const SecondaryNavigationList: FC<{
|
export const ConfigurationNavigationList: FC<{
|
||||||
routes: INavigationMenuItem[];
|
routes: INavigationMenuItem[];
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
onClick: (activeItem: string) => void;
|
onClick: (activeItem: string) => void;
|
||||||
activeItem?: string;
|
activeItem?: string;
|
||||||
}> = ({ routes, mode, onClick, activeItem }) => {
|
}> = ({ routes, mode, onClick, activeItem }) => {
|
||||||
const showBadge = useShowBadge();
|
const showBadge = useShowBadge();
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
|
||||||
const sideMenuCleanup = useUiFlag('sideMenuCleanup');
|
const sideMenuCleanup = useUiFlag('sideMenuCleanup');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
{routes.map((route) => (
|
{routes.map((route) => (
|
||||||
<DynamicListItem
|
<MenuListItem
|
||||||
key={route.title}
|
key={route.title}
|
||||||
onClick={() => onClick(route.path)}
|
onClick={() => onClick(route.path)}
|
||||||
href={route.path}
|
href={route.path}
|
||||||
@ -33,13 +32,16 @@ export const SecondaryNavigationList: FC<{
|
|||||||
<EnterprisePlanBadge />
|
<EnterprisePlanBadge />
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
>
|
mode={mode}
|
||||||
{sideMenuCleanup ? (
|
icon={
|
||||||
|
sideMenuCleanup ? (
|
||||||
<StopRoundedIcon fontSize='small' color='primary' />
|
<StopRoundedIcon fontSize='small' color='primary' />
|
||||||
) : (
|
) : (
|
||||||
<IconRenderer path={route.path} />
|
<IconRenderer path={route.path} />
|
||||||
)}
|
)
|
||||||
</DynamicListItem>
|
}
|
||||||
|
secondary={sideMenuCleanup}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
@ -1,6 +1,8 @@
|
|||||||
import type React from 'react';
|
|
||||||
import type { FC, ReactNode } from 'react';
|
import type { FC, ReactNode } from 'react';
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
@ -13,6 +15,8 @@ import { Link } from 'react-router-dom';
|
|||||||
import { basePath } from 'utils/formatPath';
|
import { basePath } from 'utils/formatPath';
|
||||||
import SignOutIcon from '@mui/icons-material/ExitToApp';
|
import SignOutIcon from '@mui/icons-material/ExitToApp';
|
||||||
import type { Theme } from '@mui/material/styles/createTheme';
|
import type { Theme } from '@mui/material/styles/createTheme';
|
||||||
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
|
import type { NavigationMode } from './NavigationMode.tsx';
|
||||||
|
|
||||||
const listItemButtonStyle = (theme: Theme) => ({
|
const listItemButtonStyle = (theme: Theme) => ({
|
||||||
borderRadius: theme.spacing(0.5),
|
borderRadius: theme.spacing(0.5),
|
||||||
@ -22,12 +26,17 @@ const listItemButtonStyle = (theme: Theme) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const CappedText = styled(Typography)({
|
const CappedText = styled(Typography)<{
|
||||||
|
bold?: boolean;
|
||||||
|
}>(({ theme, bold }) => ({
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
});
|
fontWeight: bold
|
||||||
|
? theme.typography.fontWeightBold
|
||||||
|
: theme.typography.fontWeightRegular,
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledListItemIcon = styled(ListItemIcon)(({ theme }) => ({
|
const StyledListItemIcon = styled(ListItemIcon)(({ theme }) => ({
|
||||||
minWidth: theme.spacing(4),
|
minWidth: theme.spacing(4),
|
||||||
@ -38,37 +47,10 @@ const StyledListItemText = styled(ListItemText)(({ theme }) => ({
|
|||||||
margin: 0,
|
margin: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FullListItem: FC<{
|
|
||||||
href: string;
|
|
||||||
text: string;
|
|
||||||
badge?: ReactNode;
|
|
||||||
onClick: () => void;
|
|
||||||
selected?: boolean;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}> = ({ href, text, badge, onClick, selected, children }) => {
|
|
||||||
return (
|
|
||||||
<ListItem disablePadding onClick={onClick}>
|
|
||||||
<ListItemButton
|
|
||||||
dense={true}
|
|
||||||
component={Link}
|
|
||||||
to={href}
|
|
||||||
sx={listItemButtonStyle}
|
|
||||||
selected={selected}
|
|
||||||
>
|
|
||||||
<StyledListItemIcon>{children}</StyledListItemIcon>
|
|
||||||
<StyledListItemText>
|
|
||||||
<CappedText>{text}</CappedText>
|
|
||||||
</StyledListItemText>
|
|
||||||
{badge}
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ExternalFullListItem: FC<{
|
export const ExternalFullListItem: FC<{
|
||||||
href: string;
|
href: string;
|
||||||
text: string;
|
text: string;
|
||||||
children?: React.ReactNode;
|
children?: ReactNode;
|
||||||
}> = ({ href, text, children }) => {
|
}> = ({ href, text, children }) => {
|
||||||
return (
|
return (
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
@ -88,6 +70,7 @@ export const ExternalFullListItem: FC<{
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignOutItem = () => {
|
export const SignOutItem = () => {
|
||||||
return (
|
return (
|
||||||
<form method='POST' action={`${basePath}/logout`}>
|
<form method='POST' action={`${basePath}/logout`}>
|
||||||
@ -110,26 +93,139 @@ export const SignOutItem = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MiniListItem: FC<{
|
export const MenuListItem: FC<{
|
||||||
href: string;
|
href: string;
|
||||||
text: string;
|
text: string;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
|
badge?: ReactNode;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
children?: React.ReactNode;
|
icon?: ReactNode;
|
||||||
}> = ({ href, text, selected, onClick, children }) => {
|
children?: ReactNode;
|
||||||
|
mode?: NavigationMode;
|
||||||
|
secondary?: boolean;
|
||||||
|
}> = ({
|
||||||
|
href,
|
||||||
|
text,
|
||||||
|
selected,
|
||||||
|
onClick,
|
||||||
|
icon,
|
||||||
|
mode = 'full',
|
||||||
|
badge,
|
||||||
|
children,
|
||||||
|
secondary,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ListItem disablePadding onClick={onClick}>
|
<ListItem disablePadding onClick={onClick}>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
dense={true}
|
dense
|
||||||
component={Link}
|
component={Link}
|
||||||
to={href}
|
to={href}
|
||||||
sx={listItemButtonStyle}
|
sx={(theme) => ({
|
||||||
|
...listItemButtonStyle(theme),
|
||||||
|
...(mode === 'full' &&
|
||||||
|
secondary && {
|
||||||
|
paddingLeft: theme.spacing(4),
|
||||||
|
}),
|
||||||
|
})}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
>
|
>
|
||||||
|
{mode === 'mini' ? (
|
||||||
<Tooltip title={text} placement='right'>
|
<Tooltip title={text} placement='right'>
|
||||||
<StyledListItemIcon>{children}</StyledListItemIcon>
|
<StyledListItemIcon>{icon}</StyledListItemIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<StyledListItemIcon>{icon}</StyledListItemIcon>
|
||||||
|
<StyledListItemText>
|
||||||
|
<CappedText>{text}</CappedText>
|
||||||
|
</StyledListItemText>
|
||||||
|
{badge}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
{children}
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledAccordion = styled(Accordion)(({ theme }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
'.MuiAccordionSummary-root': {
|
||||||
|
minHeight: 'auto',
|
||||||
|
borderRadius: theme.spacing(1),
|
||||||
|
borderLeft: `${theme.spacing(0.5)} solid transparent`,
|
||||||
|
margin: 0,
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
paddingBottom: theme.spacing(0.5),
|
||||||
|
'.MuiAccordionSummary-content': { margin: 0 },
|
||||||
|
'&>.MuiAccordionSummary-content.MuiAccordionSummary-content': {
|
||||||
|
margin: '0',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'.MuiAccordionSummary-content': {
|
||||||
|
margin: 0,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
'.MuiAccordionSummary-expandIconWrapper': {
|
||||||
|
position: 'absolute',
|
||||||
|
right: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const MenuListAccordion: FC<{
|
||||||
|
title: string;
|
||||||
|
expanded: boolean;
|
||||||
|
onExpandChange: (expanded: boolean) => void;
|
||||||
|
children?: ReactNode;
|
||||||
|
mode?: NavigationMode;
|
||||||
|
icon?: ReactNode;
|
||||||
|
active?: boolean;
|
||||||
|
}> = ({ title, expanded, mode, icon, onExpandChange, children, active }) => {
|
||||||
|
return (
|
||||||
|
<ListItem disablePadding sx={{ display: 'flex' }}>
|
||||||
|
<StyledAccordion
|
||||||
|
disableGutters={true}
|
||||||
|
sx={{
|
||||||
|
boxShadow: 'none',
|
||||||
|
'&:before': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
expanded={expanded}
|
||||||
|
onChange={(_, expand) => {
|
||||||
|
onExpandChange(expand);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
sx={{ padding: 0 }}
|
||||||
|
expandIcon={mode === 'full' ? <ExpandMoreIcon /> : null}
|
||||||
|
>
|
||||||
|
<ListItemButton
|
||||||
|
dense
|
||||||
|
sx={listItemButtonStyle}
|
||||||
|
selected={active && mode === 'mini'}
|
||||||
|
disableRipple
|
||||||
|
>
|
||||||
|
{mode === 'mini' ? (
|
||||||
|
<Tooltip title={title} placement='right'>
|
||||||
|
<StyledListItemIcon>{icon}</StyledListItemIcon>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<StyledListItemIcon>{icon}</StyledListItemIcon>
|
||||||
|
<StyledListItemText>
|
||||||
|
<CappedText bold={active}>
|
||||||
|
{title}
|
||||||
|
</CappedText>
|
||||||
|
</StyledListItemText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ListItemButton>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails sx={{ p: 0 }}>{children}</AccordionDetails>
|
||||||
|
</StyledAccordion>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,24 +5,32 @@ import {
|
|||||||
OtherLinksList,
|
OtherLinksList,
|
||||||
} from './NavigationList.tsx';
|
} from './NavigationList.tsx';
|
||||||
import type { NewInUnleash } from './NewInUnleash/NewInUnleash.tsx';
|
import type { NewInUnleash } from './NewInUnleash/NewInUnleash.tsx';
|
||||||
import { SecondaryNavigationList } from './SecondaryNavigationList.tsx';
|
import { ConfigurationNavigationList } from './ConfigurationNavigationList.tsx';
|
||||||
import { useRoutes } from './useRoutes.ts';
|
import { useRoutes } from './useRoutes.ts';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
|
||||||
export const MobileNavigationSidebar: FC<{
|
export const MobileNavigationSidebar: FC<{
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
NewInUnleash?: typeof NewInUnleash;
|
NewInUnleash?: typeof NewInUnleash;
|
||||||
}> = ({ onClick, NewInUnleash }) => {
|
}> = ({ onClick, NewInUnleash }) => {
|
||||||
const { routes } = useRoutes();
|
const { routes } = useRoutes();
|
||||||
|
const sideMenuCleanup = useUiFlag('sideMenuCleanup');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{NewInUnleash ? <NewInUnleash /> : null}
|
{NewInUnleash ? <NewInUnleash /> : null}
|
||||||
<PrimaryNavigationList mode='full' onClick={onClick} />
|
<PrimaryNavigationList
|
||||||
<SecondaryNavigationList
|
mode='full'
|
||||||
|
onClick={onClick}
|
||||||
|
setMode={() => {}}
|
||||||
|
/>
|
||||||
|
{!sideMenuCleanup ? (
|
||||||
|
<ConfigurationNavigationList
|
||||||
routes={routes.mainNavRoutes}
|
routes={routes.mainNavRoutes}
|
||||||
mode='full'
|
mode='full'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
|
) : null}
|
||||||
<AdminSettingsLink mode={'full'} onClick={onClick} />
|
<AdminSettingsLink mode={'full'} onClick={onClick} />
|
||||||
<OtherLinksList />
|
<OtherLinksList />
|
||||||
</>
|
</>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import type { FC } from 'react';
|
import type { ComponentProps, FC } from 'react';
|
||||||
import type { INavigationMenuItem } from 'interfaces/route';
|
import type { INavigationMenuItem } from 'interfaces/route';
|
||||||
import type { NavigationMode } from './NavigationMode.tsx';
|
import type { NavigationMode } from './NavigationMode.tsx';
|
||||||
import {
|
import {
|
||||||
ExternalFullListItem,
|
ExternalFullListItem,
|
||||||
FullListItem,
|
MenuListItem,
|
||||||
MiniListItem,
|
|
||||||
SignOutItem,
|
SignOutItem,
|
||||||
} from './ListItems.tsx';
|
} from './ListItems.tsx';
|
||||||
import { Box, List, Typography } from '@mui/material';
|
import { Box, List, Typography } from '@mui/material';
|
||||||
@ -16,6 +15,7 @@ import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectO
|
|||||||
import { useNewAdminMenu } from 'hooks/useNewAdminMenu';
|
import { useNewAdminMenu } from 'hooks/useNewAdminMenu';
|
||||||
import { AdminMenuNavigation } from '../AdminMenu/AdminNavigationItems.tsx';
|
import { AdminMenuNavigation } from '../AdminMenu/AdminNavigationItems.tsx';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
import { ConfigurationAccordion } from './ConfigurationAccordion.tsx';
|
||||||
|
|
||||||
export const OtherLinksList = () => {
|
export const OtherLinksList = () => {
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
@ -36,105 +36,92 @@ export const OtherLinksList = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated remove with `sideMenuCleanup` flag
|
||||||
|
*/
|
||||||
export const RecentProjectsList: FC<{
|
export const RecentProjectsList: FC<{
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}> = ({ projectId, projectName, mode, onClick }) => {
|
}> = ({ projectId, projectName, mode, onClick }) => (
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List>
|
<List>
|
||||||
<DynamicListItem
|
<MenuListItem
|
||||||
href={`/projects/${projectId}`}
|
href={`/projects/${projectId}`}
|
||||||
text={projectName}
|
text={projectName}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={false}
|
selected={false}
|
||||||
>
|
mode={mode}
|
||||||
<ProjectIcon />
|
icon={<ProjectIcon />}
|
||||||
</DynamicListItem>
|
/>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated remove with `sideMenuCleanup` flag
|
||||||
|
*/
|
||||||
export const RecentFlagsList: FC<{
|
export const RecentFlagsList: FC<{
|
||||||
flags: { featureId: string; projectId: string }[];
|
flags: { featureId: string; projectId: string }[];
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}> = ({ flags, mode, onClick }) => {
|
}> = ({ flags, mode, onClick }) => (
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List>
|
<List>
|
||||||
{flags.map((flag) => (
|
{flags.map((flag) => (
|
||||||
<DynamicListItem
|
<MenuListItem
|
||||||
href={`/projects/${flag.projectId}/features/${flag.featureId}`}
|
href={`/projects/${flag.projectId}/features/${flag.featureId}`}
|
||||||
text={flag.featureId}
|
text={flag.featureId}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={false}
|
selected={false}
|
||||||
key={flag.featureId}
|
key={flag.featureId}
|
||||||
>
|
mode={mode}
|
||||||
<FlagIcon />
|
icon={<FlagIcon />}
|
||||||
</DynamicListItem>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export const PrimaryNavigationList: FC<{
|
export const PrimaryNavigationList: FC<{
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
|
setMode: (mode: NavigationMode) => void;
|
||||||
onClick: (activeItem: string) => void;
|
onClick: (activeItem: string) => void;
|
||||||
activeItem?: string;
|
activeItem?: string;
|
||||||
}> = ({ mode, onClick, activeItem }) => {
|
}> = ({ mode, setMode, onClick, activeItem }) => {
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
const PrimaryListItem = ({
|
||||||
|
href,
|
||||||
|
text,
|
||||||
|
}: Pick<ComponentProps<typeof MenuListItem>, 'href' | 'text'>) => (
|
||||||
|
<MenuListItem
|
||||||
|
href={href}
|
||||||
|
text={text}
|
||||||
|
icon={<IconRenderer path={href} />}
|
||||||
|
onClick={() => onClick(href)}
|
||||||
|
selected={activeItem === href}
|
||||||
|
mode={mode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
const { isOss } = useUiConfig();
|
const { isOss } = useUiConfig();
|
||||||
const sideMenuCleanup = useUiFlag('sideMenuCleanup');
|
const sideMenuCleanup = useUiFlag('sideMenuCleanup');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
<DynamicListItem
|
<PrimaryListItem href='/personal' text='Dashboard' />
|
||||||
href='/personal'
|
<PrimaryListItem href='/projects' text='Projects' />
|
||||||
text='Dashboard'
|
<PrimaryListItem href='/search' text='Flags overview' />
|
||||||
onClick={() => onClick('/personal')}
|
<PrimaryListItem href='/playground' text='Playground' />
|
||||||
selected={activeItem === '/personal'}
|
|
||||||
>
|
|
||||||
<IconRenderer path='/personal' />
|
|
||||||
</DynamicListItem>
|
|
||||||
|
|
||||||
<DynamicListItem
|
|
||||||
href='/projects'
|
|
||||||
text='Projects'
|
|
||||||
onClick={() => onClick('/projects')}
|
|
||||||
selected={activeItem === '/projects'}
|
|
||||||
>
|
|
||||||
<IconRenderer path='/projects' />
|
|
||||||
</DynamicListItem>
|
|
||||||
<DynamicListItem
|
|
||||||
href='/search'
|
|
||||||
text='Flags overview'
|
|
||||||
onClick={() => onClick('/search')}
|
|
||||||
selected={activeItem === '/search'}
|
|
||||||
>
|
|
||||||
<IconRenderer path='/search' />
|
|
||||||
</DynamicListItem>
|
|
||||||
<DynamicListItem
|
|
||||||
href='/playground'
|
|
||||||
text='Playground'
|
|
||||||
onClick={() => onClick('/playground')}
|
|
||||||
selected={activeItem === '/playground'}
|
|
||||||
>
|
|
||||||
<IconRenderer path='/playground' />
|
|
||||||
</DynamicListItem>
|
|
||||||
{!isOss() ? (
|
{!isOss() ? (
|
||||||
<DynamicListItem
|
<PrimaryListItem
|
||||||
href='/insights'
|
href='/insights'
|
||||||
text={sideMenuCleanup ? 'Analytics' : 'Insights'}
|
text={sideMenuCleanup ? 'Analytics' : 'Insights'}
|
||||||
onClick={() => onClick('/insights')}
|
/>
|
||||||
selected={activeItem === '/insights'}
|
) : null}
|
||||||
>
|
{sideMenuCleanup ? (
|
||||||
<IconRenderer path='/insights' />
|
<ConfigurationAccordion
|
||||||
</DynamicListItem>
|
mode={mode}
|
||||||
|
setMode={setMode}
|
||||||
|
activeItem={activeItem}
|
||||||
|
onClick={() => onClick('configure')}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
@ -173,22 +160,19 @@ export const AdminSettingsNavigation: FC<{
|
|||||||
export const AdminSettingsLink: FC<{
|
export const AdminSettingsLink: FC<{
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
onClick: (activeItem: string) => void;
|
onClick: (activeItem: string) => void;
|
||||||
}> = ({ mode, onClick }) => {
|
}> = ({ mode, onClick }) => (
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
|
||||||
return (
|
|
||||||
<Box>
|
<Box>
|
||||||
<List>
|
<List>
|
||||||
<DynamicListItem
|
<MenuListItem
|
||||||
href='/admin'
|
href='/admin'
|
||||||
text='Admin settings'
|
text='Admin settings'
|
||||||
onClick={() => onClick('/admin')}
|
onClick={() => onClick('/admin')}
|
||||||
>
|
mode={mode}
|
||||||
<IconRenderer path='/admin' />
|
icon={<IconRenderer path='/admin' />}
|
||||||
</DynamicListItem>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export const RecentProjectsNavigation: FC<{
|
export const RecentProjectsNavigation: FC<{
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
|
@ -10,9 +10,8 @@ import {
|
|||||||
RecentProjectsNavigation,
|
RecentProjectsNavigation,
|
||||||
AdminSettingsNavigation,
|
AdminSettingsNavigation,
|
||||||
} from './NavigationList.tsx';
|
} from './NavigationList.tsx';
|
||||||
import { SecondaryNavigationList } from './SecondaryNavigationList.tsx';
|
import { ConfigurationNavigationList } from './ConfigurationNavigationList.tsx';
|
||||||
import { SecondaryNavigation } from './SecondaryNavigation.tsx';
|
import { ConfigurationNavigation } from './ConfigurationNavigation.tsx';
|
||||||
import { FullListItem, MiniListItem } from './ListItems.tsx';
|
|
||||||
import { useInitialPathname } from './useInitialPathname.ts';
|
import { useInitialPathname } from './useInitialPathname.ts';
|
||||||
import { useLastViewedProject } from 'hooks/useLastViewedProject';
|
import { useLastViewedProject } from 'hooks/useLastViewedProject';
|
||||||
import { useLastViewedFlags } from 'hooks/useLastViewedFlags';
|
import { useLastViewedFlags } from 'hooks/useLastViewedFlags';
|
||||||
@ -105,7 +104,6 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
|||||||
const { lastViewed: lastViewedFlags } = useLastViewedFlags();
|
const { lastViewed: lastViewedFlags } = useLastViewedFlags();
|
||||||
const showRecentFlags =
|
const showRecentFlags =
|
||||||
!sideMenuCleanup && mode === 'full' && lastViewedFlags.length > 0;
|
!sideMenuCleanup && mode === 'full' && lastViewedFlags.length > 0;
|
||||||
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveItem(initialPathname);
|
setActiveItem(initialPathname);
|
||||||
@ -155,10 +153,12 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
|||||||
<>
|
<>
|
||||||
<PrimaryNavigationList
|
<PrimaryNavigationList
|
||||||
mode={mode}
|
mode={mode}
|
||||||
|
setMode={setMode}
|
||||||
onClick={setActiveItem}
|
onClick={setActiveItem}
|
||||||
activeItem={activeItem}
|
activeItem={activeItem}
|
||||||
/>
|
/>
|
||||||
<SecondaryNavigation
|
{!sideMenuCleanup ? (
|
||||||
|
<ConfigurationNavigation
|
||||||
expanded={expanded.includes('configure')}
|
expanded={expanded.includes('configure')}
|
||||||
onExpandChange={(expand) => {
|
onExpandChange={(expand) => {
|
||||||
changeExpanded('configure', expand);
|
changeExpanded('configure', expand);
|
||||||
@ -166,13 +166,14 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
|||||||
mode={mode}
|
mode={mode}
|
||||||
title='Configure'
|
title='Configure'
|
||||||
>
|
>
|
||||||
<SecondaryNavigationList
|
<ConfigurationNavigationList
|
||||||
routes={routes.mainNavRoutes}
|
routes={routes.mainNavRoutes}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
onClick={setActiveItem}
|
onClick={setActiveItem}
|
||||||
activeItem={activeItem}
|
activeItem={activeItem}
|
||||||
/>
|
/>
|
||||||
</SecondaryNavigation>
|
</ConfigurationNavigation>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<AdminSettingsNavigation
|
<AdminSettingsNavigation
|
||||||
onClick={setActiveItem}
|
onClick={setActiveItem}
|
||||||
|
Loading…
Reference in New Issue
Block a user