refactor: monorepo setup with lerna (#243)
This commit is contained in:
committed by
GitHub
parent
dac29fbf3c
commit
504d95c34f
@ -0,0 +1,127 @@
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import MobileNavLink from './MobileNavLink';
|
||||
|
||||
import type { MouseEvent } from 'react';
|
||||
import type { MenuItem, MenuLink, MenuLinkGroup } from '../../../interface';
|
||||
|
||||
interface MobileNavItemProps {
|
||||
item: MenuItem;
|
||||
}
|
||||
|
||||
function isMenuLinkGroup(link: MenuItem): link is MenuLinkGroup {
|
||||
return 'groups' in link;
|
||||
}
|
||||
|
||||
const MobileNavItem = ({ item }: MobileNavItemProps) => {
|
||||
const theme = useTheme();
|
||||
const { asPath } = useRouter();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
if ('url' in item) {
|
||||
return asPath === item.url;
|
||||
}
|
||||
|
||||
return asPath.startsWith(item.path);
|
||||
}, [asPath, item]);
|
||||
|
||||
const [open, setOpen] = useState(selected);
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(link: MenuItem | MenuLink) => (event: MouseEvent) => {
|
||||
if (isMenuLinkGroup(link)) {
|
||||
event.stopPropagation();
|
||||
setOpen(!open);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[open],
|
||||
);
|
||||
|
||||
const url = useMemo(() => {
|
||||
if (isMenuLinkGroup(item)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return item.url;
|
||||
}, [item]);
|
||||
|
||||
const wrappedLink = useMemo(
|
||||
() => (
|
||||
<ListItemButton
|
||||
component={url ? Link : 'button'}
|
||||
href={url}
|
||||
target={url?.startsWith('http') ? '_blank' : undefined}
|
||||
key={`drawer-nav-item-${item.title}`}
|
||||
onClick={handleOnClick(item)}
|
||||
selected={selected}
|
||||
>
|
||||
<ListItemText primary={item.title} />
|
||||
{isMenuLinkGroup(item) ? (
|
||||
<ExpandLessIcon
|
||||
sx={{
|
||||
transform: `rotateZ(${open ? 0 : 90}deg)`,
|
||||
transition: theme.transitions.create(['transform']),
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</ListItemButton>
|
||||
),
|
||||
[handleOnClick, item, open, selected, theme.transitions, url],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{wrappedLink}
|
||||
{isMenuLinkGroup(item) ? (
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
{item.groups.map(group => (
|
||||
<List
|
||||
key={group.title}
|
||||
component="div"
|
||||
subheader={
|
||||
<ListSubheader
|
||||
component="div"
|
||||
id="nested-list-subheader"
|
||||
sx={{
|
||||
lineHeight: '32px',
|
||||
textTransform: 'uppercase',
|
||||
top: '-1px',
|
||||
}}
|
||||
>
|
||||
{group.title}
|
||||
</ListSubheader>
|
||||
}
|
||||
disablePadding
|
||||
sx={{
|
||||
marginTop: '8px',
|
||||
'&:not(:first-of-type)': {
|
||||
marginTop: '20px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{group.links.map(link => (
|
||||
<MobileNavLink
|
||||
key={`drawer-nav-item-${item.title}-sub-item-${link.title}`}
|
||||
link={link}
|
||||
onClick={handleOnClick(link)}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
))}
|
||||
</Collapse>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNavItem;
|
@ -0,0 +1,37 @@
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { MouseEvent } from 'react';
|
||||
import type { MenuLink } from '../../../interface';
|
||||
|
||||
interface MobileNavLinkProps {
|
||||
link: MenuLink;
|
||||
onClick: (event: MouseEvent) => void;
|
||||
}
|
||||
|
||||
const MobileNavLink = ({ link, onClick }: MobileNavLinkProps) => {
|
||||
const { title, url } = link;
|
||||
const { asPath } = useRouter();
|
||||
|
||||
const selected = useMemo(() => {
|
||||
return asPath === url;
|
||||
}, [asPath, url]);
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
href={url}
|
||||
target={url.startsWith('http') ? '_blank' : undefined}
|
||||
sx={{ paddingLeft: '24px', paddingTop: '4px', paddingBottom: '4px' }}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
>
|
||||
<ListItemText primary={title} />
|
||||
</ListItemButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileNavLink;
|
@ -0,0 +1,104 @@
|
||||
import Divider from '@mui/material/Divider';
|
||||
import List from '@mui/material/List';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import SwipeableDrawer from '@mui/material/SwipeableDrawer';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import MobileNavItem from './MobileNavItem';
|
||||
import Logo from '../Logo';
|
||||
|
||||
import type { MenuItem } from '../../../interface';
|
||||
|
||||
const DRAWER_WIDTH = 300;
|
||||
|
||||
const StyledDrawerContents = styled('div')`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledLogoWrapper = styled('div')(
|
||||
({ theme }) => `
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px 0;
|
||||
background: ${
|
||||
theme.palette.mode === 'light' ? theme.palette.primary.main : theme.palette.background.paper
|
||||
};
|
||||
`,
|
||||
);
|
||||
|
||||
interface NavigationDrawerProps {
|
||||
items: MenuItem[];
|
||||
mobileOpen: boolean;
|
||||
onMobileOpenToggle: () => void;
|
||||
}
|
||||
|
||||
const NavigationDrawer = ({ items, mobileOpen, onMobileOpenToggle }: NavigationDrawerProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const iOS = useMemo(
|
||||
() => typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent),
|
||||
[],
|
||||
);
|
||||
|
||||
const drawer = useMemo(
|
||||
() => (
|
||||
<StyledDrawerContents key="drawer-nav-contents" onClick={onMobileOpenToggle}>
|
||||
<StyledLogoWrapper key="drawer-nav-logo-wrapper">
|
||||
<Logo key="drawer-nav-logo" />
|
||||
</StyledLogoWrapper>
|
||||
<Divider key="drawer-nav-divider" sx={{ borderColor: 'rgba(255, 255, 255, 0.8)' }} />
|
||||
<List key="drawer-nav-list">
|
||||
{items.map(item => (
|
||||
<MobileNavItem key={`drawer-nav-item-${item.title}`} item={item} />
|
||||
))}
|
||||
</List>
|
||||
</StyledDrawerContents>
|
||||
),
|
||||
[items, onMobileOpenToggle],
|
||||
);
|
||||
|
||||
const container = useMemo(
|
||||
() => (typeof window !== 'undefined' ? window.document.body : undefined),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<SwipeableDrawer
|
||||
key="swipable-drawer"
|
||||
disableBackdropTransition={!iOS}
|
||||
disableDiscovery={iOS}
|
||||
container={container}
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onOpen={onMobileOpenToggle}
|
||||
onClose={onMobileOpenToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: 'none',
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
display: 'block',
|
||||
},
|
||||
width: '80%',
|
||||
maxWidth: DRAWER_WIDTH,
|
||||
'& .MuiBackdrop-root': {
|
||||
width: '100%',
|
||||
},
|
||||
'& .MuiDrawer-paper': {
|
||||
boxSizing: 'border-box',
|
||||
width: '80%',
|
||||
maxWidth: DRAWER_WIDTH,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
'& .MuiListSubheader-root': {
|
||||
textAlign: 'left',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</SwipeableDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationDrawer;
|
Reference in New Issue
Block a user