Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add breadcrumb navigation #118

Merged
merged 4 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ const MessagesPage: NextPage<{
return (
<Box sx={{ display: 'flex', minHeight: '100dvh' }}>
<SidebarContextProvider>
<Header />
<Header
languageName={languageName}
messageId={messageId}
projectName={projectName}
/>
<Sidebar>
<TitleBar languageName={languageName} projectName={projectName} />
<PullRequestButton projectName={projectName} />
Expand Down
59 changes: 59 additions & 0 deletions webapp/src/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { FC, useMemo } from 'react';
import { Breadcrumbs as MuiBreadcrumbs, Link } from '@mui/material';

type BreadcrumbsProps = {
languageName: string;
messageId?: string;
projectName: string;
};

type Breadcrumb = {
href: string;
last: boolean;
text: string;
};

const Breadcrumbs: FC<BreadcrumbsProps> = ({
languageName,
messageId,
projectName,
}) => {
const breadcrumbs: Breadcrumb[] = useMemo(
() =>
(messageId?.split('.') || []).map((part, i) => {
const href = `/projects/${projectName}/${languageName}/${messageId!
.split('.')
.slice(0, i + 1)
.join('.')}`;
return {
href,
last: i === messageId!.split('.').length - 1,
text: part,
};
}, []),
[languageName, messageId, projectName],
);

return (
<MuiBreadcrumbs
aria-label="Breadcrumb navigation"
sx={{
maxWidth: '100%',
overflow: 'hidden',
}}
>
{breadcrumbs.map((breadcrumb) => (
<Link
key={breadcrumb.href}
color={breadcrumb.last ? 'text.primary' : 'text.secondary'}
href={breadcrumb.href}
underline="hover"
>
{breadcrumb.text}
</Link>
))}
</MuiBreadcrumbs>
);
};

export default Breadcrumbs;
44 changes: 30 additions & 14 deletions webapp/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
'use client';

import { FC, useEffect, useCallback, useContext } from 'react';
import GlobalStyles from '@mui/material/GlobalStyles';
import IconButton from '@mui/material/IconButton';
import { Box, GlobalStyles, IconButton, useTheme } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import Paper from '@mui/material/Paper';

import { SidebarContext } from './SidebarContext';
import Breadcrumbs from './Breadcrumbs';

const Header: FC = () => {
type HeaderProps = {
languageName: string;
messageId?: string;
projectName: string;
};

const Header: FC<HeaderProps> = ({ languageName, messageId, projectName }) => {
const theme = useTheme();
const { isSidebarOpen, setIsSidebarOpen } = useContext(SidebarContext);
const onResize = useCallback(() => {
if (window.innerWidth >= 960 && isSidebarOpen) {
Expand All @@ -24,41 +30,51 @@ const Header: FC = () => {
}, [onResize]);

return (
<Paper
<Box
sx={{
alignItems: 'center',
borderBottom: 1,
boxShadow: 1,
display: { md: 'none', xs: 'flex' },
display: 'flex',
flexDirection: 'row',
gap: 1,
height: 'var(--Header-height)',
justifyContent: 'flex-end',
p: 2,
justifyContent: 'space-between',
position: 'fixed',
px: 2,
top: 0,
width: '100vw',
zIndex: 9995,
[theme.breakpoints.up('md')]: {
marginLeft: 'var(--Sidebar-width)',
width: 'calc(100vw - var(--Sidebar-width))',
},
}}
>
<GlobalStyles
styles={(theme) => ({
styles={{
':root': {
'--Header-height': '52px',
[theme.breakpoints.up('md')]: {
'--Header-height': '0px',
},
},
})}
}}
/>
<Breadcrumbs
languageName={languageName}
messageId={messageId}
projectName={projectName}
/>
<IconButton
aria-label="Menu"
color="primary"
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
size="small"
sx={{
display: { md: 'none', xs: 'flex' },
}}
>
<MenuIcon />
</IconButton>
</Paper>
</Box>
);
};

Expand Down
5 changes: 0 additions & 5 deletions webapp/src/components/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { useTheme } from '@mui/material';
import Box from '@mui/material/Box';
import { FC, ReactNode } from 'react';

Expand All @@ -9,7 +8,6 @@ type MainProps = {
};

const Main: FC<MainProps> = ({ children }) => {
const theme = useTheme();
return (
<Box
component="main"
Expand All @@ -18,9 +16,6 @@ const Main: FC<MainProps> = ({ children }) => {
flex: 1,
flexDirection: 'column',
marginTop: 6,
[theme.breakpoints.up('md')]: {
marginTop: 0,
},
}}
>
{children}
Expand Down
6 changes: 3 additions & 3 deletions webapp/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { Box, useTheme } from '@mui/material';
import GlobalStyles from '@mui/material/GlobalStyles';
import { FocusTrap } from '@mui/base/FocusTrap';
import Paper from '@mui/material/Paper';
import {
FC,
ReactNode,
Expand Down Expand Up @@ -48,8 +47,9 @@ const Sidebar: FC<SidebarProps> = ({ children }) => {

return (
<FocusTrap open={openOnMobile}>
<Paper
<Box
sx={{
backgroundColor: 'background.paper',
borderColor: 'divider',
borderRight: '1px solid',
display: 'flex',
Expand Down Expand Up @@ -91,7 +91,7 @@ const Sidebar: FC<SidebarProps> = ({ children }) => {
>
{children}
</Box>
</Paper>
</Box>
</FocusTrap>
);
};
Expand Down
8 changes: 8 additions & 0 deletions webapp/src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ const roboto = Roboto({

const theme = createTheme({
components: {
MuiBreadcrumbs: {
styleOverrides: {
ol: () => ({
flexWrap: 'nowrap',
justifyContent: 'flex-end',
}),
},
},
MuiLinearProgress: {
styleOverrides: {
root: () => ({
Expand Down