mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-10 01:16:39 +02:00
https://linear.app/unleash/issue/2-2662/make-the-event-timeline-available-globally-through-a-new-header-button https://github.com/user-attachments/assets/bde38ee8-cdd8-409d-a95e-0c06189e3d9b (In the video, you’ll notice a slight delay before new events show up. This happens because the timeline automatically refreshes every 10 seconds) Removes the event timeline from the event log and integrates it into a new header option. I chose a middle-ground approach between options 1 and 2 from our Figma sketches. This solution provides the best of both worlds IMO: the timeline stands out as a global component, distinct from the current page context, while sliding in rather than overlapping the content. This way, users can view the timeline alongside the page content.
263 lines
9.9 KiB
TypeScript
263 lines
9.9 KiB
TypeScript
import { useState } from 'react';
|
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import { Link } from 'react-router-dom';
|
|
import {
|
|
AppBar,
|
|
Box,
|
|
Divider,
|
|
IconButton,
|
|
styled,
|
|
Tooltip,
|
|
} from '@mui/material';
|
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
import UserProfile from 'component/user/UserProfile';
|
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
import MenuBookIcon from '@mui/icons-material/MenuBook';
|
|
import { ReactComponent as UnleashLogo } from 'assets/img/logoDarkWithText.svg';
|
|
import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText.svg';
|
|
import { ReactComponent as CelebatoryUnleashLogo } from 'assets/img/unleashHoliday.svg';
|
|
import { ReactComponent as CelebatoryUnleashLogoWhite } from 'assets/img/unleashHolidayDark.svg';
|
|
|
|
import { DrawerMenu } from './DrawerMenu/DrawerMenu';
|
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|
import { flexRow, focusable } from 'themes/themeStyles';
|
|
import { getCondensedRoutes, getRoutes } from 'component/menu/routes';
|
|
import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined';
|
|
import LightModeOutlined from '@mui/icons-material/LightModeOutlined';
|
|
import { filterByConfig, mapRouteLink } from 'component/common/util';
|
|
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
|
import { useThemeMode } from 'hooks/useThemeMode';
|
|
import { Notifications } from 'component/common/Notifications/Notifications';
|
|
import { useAdminRoutes } from 'component/admin/useAdminRoutes';
|
|
import InviteLinkButton from './InviteLink/InviteLinkButton/InviteLinkButton';
|
|
import { useUiFlag } from 'hooks/useUiFlag';
|
|
import { CommandBar } from 'component/commandBar/CommandBar';
|
|
import TimelineIcon from '@mui/icons-material/Timeline';
|
|
|
|
const HeaderComponent = styled(AppBar)(({ theme }) => ({
|
|
backgroundColor: theme.palette.background.paper,
|
|
padding: theme.spacing(1),
|
|
boxShadow: 'none',
|
|
position: 'relative',
|
|
zIndex: 300,
|
|
paddingRight: theme.spacing(9),
|
|
[theme.breakpoints.down('lg')]: {
|
|
paddingLeft: theme.spacing(1),
|
|
paddingRight: theme.spacing(1),
|
|
},
|
|
[theme.breakpoints.down(1024)]: {
|
|
marginLeft: 0,
|
|
marginRight: 0,
|
|
},
|
|
[theme.breakpoints.down('sm')]: {
|
|
minWidth: '100%',
|
|
},
|
|
margin: '0 auto',
|
|
}));
|
|
|
|
const ContainerComponent = styled(Box)(() => ({
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
width: '100%',
|
|
'&&&': { padding: 0 },
|
|
}));
|
|
|
|
const StyledUserContainer = styled('div')({
|
|
marginLeft: 'auto',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
});
|
|
|
|
const StyledNav = styled('nav')({
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
flexGrow: 1,
|
|
});
|
|
|
|
const StyledUnleashLogoWhite = styled(UnleashLogoWhite)({ width: '150px' });
|
|
|
|
const StyledUnleashLogo = styled(UnleashLogo)({ width: '150px' });
|
|
|
|
const StyledCelebatoryLogo = styled(CelebatoryUnleashLogo)({ width: '150px' });
|
|
|
|
const StyledLink = styled(Link)(({ theme }) => focusable(theme));
|
|
|
|
const StyledIconButton = styled(IconButton)<{
|
|
component?: 'a' | 'button';
|
|
href?: string;
|
|
target?: string;
|
|
}>(({ theme }) => ({
|
|
borderRadius: 100,
|
|
'&:focus-visible': {
|
|
outlineStyle: 'solid',
|
|
outlineWidth: 2,
|
|
outlineColor: theme.palette.primary.main,
|
|
borderRadius: '100%',
|
|
},
|
|
}));
|
|
|
|
interface IHeaderProps {
|
|
showTimeline: boolean;
|
|
setShowTimeline: (show: boolean) => void;
|
|
}
|
|
|
|
const Header = ({ showTimeline, setShowTimeline }: IHeaderProps) => {
|
|
const { onSetThemeMode, themeMode } = useThemeMode();
|
|
const theme = useTheme();
|
|
|
|
const disableNotifications = useUiFlag('disableNotifications');
|
|
const { uiConfig, isOss } = useUiConfig();
|
|
const smallScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
|
const [openDrawer, setOpenDrawer] = useState(false);
|
|
const toggleDrawer = () => setOpenDrawer((prev) => !prev);
|
|
const celebatoryUnleash = useUiFlag('celebrateUnleash');
|
|
const eventTimeline = useUiFlag('eventTimeline') && !isOss();
|
|
|
|
const routes = getRoutes();
|
|
const adminRoutes = useAdminRoutes();
|
|
|
|
const filteredMainRoutes = {
|
|
mainNavRoutes: getCondensedRoutes(routes.mainNavRoutes)
|
|
.filter(filterByConfig(uiConfig))
|
|
.map(mapRouteLink),
|
|
mobileRoutes: getCondensedRoutes(routes.mobileRoutes)
|
|
.filter(filterByConfig(uiConfig))
|
|
.map(mapRouteLink),
|
|
adminRoutes,
|
|
};
|
|
|
|
if (smallScreen) {
|
|
return (
|
|
<HeaderComponent position='static'>
|
|
<ContainerComponent>
|
|
<Tooltip title='Menu' arrow>
|
|
<IconButton
|
|
sx={{
|
|
color: (theme) => theme.palette.text.primary,
|
|
}}
|
|
onClick={toggleDrawer}
|
|
aria-controls='header-drawer'
|
|
aria-expanded={openDrawer}
|
|
size='large'
|
|
>
|
|
<MenuIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<DrawerMenu
|
|
links={uiConfig.links}
|
|
open={openDrawer}
|
|
toggleDrawer={toggleDrawer}
|
|
routes={filteredMainRoutes}
|
|
/>
|
|
<StyledUserContainer>
|
|
<UserProfile />
|
|
</StyledUserContainer>
|
|
</ContainerComponent>
|
|
</HeaderComponent>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<HeaderComponent position='static'>
|
|
<ContainerComponent>
|
|
<StyledLink to='/' sx={flexRow} aria-label='Home'>
|
|
<ThemeMode
|
|
darkmode={
|
|
<ConditionallyRender
|
|
condition={celebatoryUnleash}
|
|
show={<CelebatoryUnleashLogoWhite />}
|
|
elseShow={
|
|
<StyledUnleashLogoWhite aria-label='Unleash logo' />
|
|
}
|
|
/>
|
|
}
|
|
lightmode={
|
|
<ConditionallyRender
|
|
condition={celebatoryUnleash}
|
|
show={<StyledCelebatoryLogo />}
|
|
elseShow={
|
|
<StyledUnleashLogo aria-label='Unleash logo' />
|
|
}
|
|
/>
|
|
}
|
|
/>
|
|
</StyledLink>
|
|
|
|
<StyledNav>
|
|
<StyledUserContainer>
|
|
<CommandBar />
|
|
<ConditionallyRender
|
|
condition={eventTimeline}
|
|
show={
|
|
<Tooltip
|
|
title={
|
|
showTimeline
|
|
? 'Hide timeline'
|
|
: 'Show timeline'
|
|
}
|
|
arrow
|
|
>
|
|
<StyledIconButton
|
|
onClick={() =>
|
|
setShowTimeline(!showTimeline)
|
|
}
|
|
size='large'
|
|
>
|
|
<TimelineIcon />
|
|
</StyledIconButton>
|
|
</Tooltip>
|
|
}
|
|
/>
|
|
<InviteLinkButton />
|
|
<Tooltip
|
|
title={
|
|
themeMode === 'dark'
|
|
? 'Switch to light theme'
|
|
: 'Switch to dark theme'
|
|
}
|
|
arrow
|
|
>
|
|
<StyledIconButton
|
|
onClick={onSetThemeMode}
|
|
size='large'
|
|
>
|
|
<ConditionallyRender
|
|
condition={themeMode === 'dark'}
|
|
show={<DarkModeOutlined />}
|
|
elseShow={<LightModeOutlined />}
|
|
/>
|
|
</StyledIconButton>
|
|
</Tooltip>
|
|
<ConditionallyRender
|
|
condition={!isOss() && !disableNotifications}
|
|
show={<Notifications />}
|
|
/>
|
|
<Tooltip title='Documentation' arrow>
|
|
<StyledIconButton
|
|
component='a'
|
|
href='https://docs.getunleash.io/'
|
|
target='_blank'
|
|
rel='noopener noreferrer'
|
|
size='large'
|
|
sx={{ mr: 1 }}
|
|
>
|
|
<MenuBookIcon />
|
|
</StyledIconButton>
|
|
</Tooltip>
|
|
<Divider
|
|
orientation='vertical'
|
|
variant='middle'
|
|
flexItem
|
|
sx={{ ml: 1 }}
|
|
/>
|
|
<UserProfile />
|
|
</StyledUserContainer>
|
|
</StyledNav>
|
|
</ContainerComponent>
|
|
</HeaderComponent>
|
|
);
|
|
};
|
|
|
|
export default Header;
|