feat: add animation for pages
This commit is contained in:
parent
50c7f4f76c
commit
5313c5c134
@ -99,6 +99,11 @@
|
||||
"**/.eslintrc.[j|t]s"
|
||||
],
|
||||
"optionalDependencies": false
|
||||
}],
|
||||
"react/function-component-definition": [
|
||||
"error", {
|
||||
"namedComponents": "arrow-function",
|
||||
"unnamedComponents": "arrow-function"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
BIN
offline_node_modules/@react-spring-animated-npm-9.7.3-4aff527e22-5151da4fa7.zip
generated
Normal file
BIN
offline_node_modules/@react-spring-animated-npm-9.7.3-4aff527e22-5151da4fa7.zip
generated
Normal file
Binary file not shown.
BIN
offline_node_modules/@react-spring-core-npm-9.7.3-00b8dd52b3-e28c05de84.zip
generated
Normal file
BIN
offline_node_modules/@react-spring-core-npm-9.7.3-00b8dd52b3-e28c05de84.zip
generated
Normal file
Binary file not shown.
BIN
offline_node_modules/@react-spring-shared-npm-9.7.3-cefcfdc40d-afb03ed28c.zip
generated
Normal file
BIN
offline_node_modules/@react-spring-shared-npm-9.7.3-cefcfdc40d-afb03ed28c.zip
generated
Normal file
Binary file not shown.
BIN
offline_node_modules/@react-spring-types-npm-9.7.3-f9de91ab6e-d645044f3c.zip
generated
Normal file
BIN
offline_node_modules/@react-spring-types-npm-9.7.3-f9de91ab6e-d645044f3c.zip
generated
Normal file
Binary file not shown.
BIN
offline_node_modules/@react-spring-web-npm-9.7.3-95390d075a-a5b4847a29.zip
generated
Normal file
BIN
offline_node_modules/@react-spring-web-npm-9.7.3-95390d075a-a5b4847a29.zip
generated
Normal file
Binary file not shown.
@ -36,6 +36,7 @@
|
||||
"@emotion/styled": "11.11.5",
|
||||
"@mui/icons-material": "^5.15.19",
|
||||
"@mui/material": "5.15.19",
|
||||
"@react-spring/web": "9.7.3",
|
||||
"classnames": "2.5.1",
|
||||
"notistack": "3.0.1",
|
||||
"prop-types": "15.8.1",
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
|
||||
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
|
||||
import PieChartIcon from '@mui/icons-material/PieChart';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { Suspense } from 'react';
|
||||
import { createBrowserRouter, Navigate, RouterProvider as RRDRouterProvider } from 'react-router-dom';
|
||||
|
||||
// todo lazy & access
|
||||
import { AnalyzePage, AnalyzePageToolbar } from 'pages/AnalyzePage';
|
||||
import { ErrorPage, ErrorPageToolbar } from 'pages/ErrorPage';
|
||||
import { PaymentsPage, PaymentsPageToolbar } from 'pages/PaymentsPage';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
|
||||
import { SchedulePage, SchedulePageToolbar } from 'pages/SchedulePage';
|
||||
|
||||
@ -7,12 +7,15 @@ describe('Test SchedulePage', () => {
|
||||
<SchedulePage data-testid="schedule-page" />,
|
||||
);
|
||||
|
||||
test('should render correctly', () => {
|
||||
test('should render correctly', async () => {
|
||||
setup();
|
||||
|
||||
const pageElem = screen.getByTestId('schedule-page');
|
||||
expect(pageElem).toBeVisible();
|
||||
expect(pageElem).toHaveTextContent('Schedule Page');
|
||||
await waitFor(() => {
|
||||
const pageElem = screen.getByTestId('schedule-page');
|
||||
expect(pageElem).toBeVisible();
|
||||
// eslint-disable-next-line testing-library/no-wait-for-multiple-assertions
|
||||
expect(pageElem).toHaveTextContent('Schedule Page');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
// todo fix eslint
|
||||
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const withProvider = <T,>(Provider: FC<PropsWithChildren>) => (Component: FC<T>) => (...params: T[]) => (
|
||||
<Provider>
|
||||
{/* @ts-ignore */}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { renderWithProvider } from '__spec__/custon-renders';
|
||||
|
||||
import PieChartIcon from '@mui/icons-material/PieChart';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
|
||||
import AsideMenuLayout from './AsideMenuLayout.ui';
|
||||
|
||||
@ -41,10 +41,6 @@ describe('Test AsideMenuLayout', () => {
|
||||
<AsideMenuLayout
|
||||
Content={<Content />}
|
||||
data-testid="aside-menu-layout"
|
||||
DrawerSwitcherProps={{
|
||||
// @ts-ignore
|
||||
'data-testid': 'drawer-switcher',
|
||||
}}
|
||||
pages={pages}
|
||||
Toolbar={<Toolbar />}
|
||||
/>,
|
||||
@ -58,18 +54,22 @@ describe('Test AsideMenuLayout', () => {
|
||||
expect(layoutElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('should render toolbar', () => {
|
||||
test('should render toolbar', async () => {
|
||||
setup();
|
||||
|
||||
const toolbarElem = screen.getByText('Test Toolbar');
|
||||
expect(toolbarElem).toBeVisible();
|
||||
await waitFor(() => {
|
||||
const toolbarElem = screen.getByText('Test Toolbar');
|
||||
expect(toolbarElem).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should render content', () => {
|
||||
test('should render content', async () => {
|
||||
setup();
|
||||
|
||||
const toolbarElem = screen.getByText('Test Content');
|
||||
expect(toolbarElem).toBeVisible();
|
||||
await waitFor(() => {
|
||||
const toolbarElem = screen.getByText('Test Content');
|
||||
expect(toolbarElem).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should render navigation routes', () => {
|
||||
|
||||
@ -1,104 +1,137 @@
|
||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import List from '@mui/material/List';
|
||||
import { useState } from 'react';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import { animated, useTransition } from '@react-spring/web';
|
||||
import { useContext } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import AppBar from './components/AppBar';
|
||||
import Drawer from './components/Drawer';
|
||||
import DrawerHeader from './components/DrawerMenu';
|
||||
import DrawerSwitcher from './components/DrawerSwitcher';
|
||||
import MenuItem from './components/MenuItem/MenuItem.ui';
|
||||
import { DesktopAppBar, MobileAppBar } from './components/AppBar';
|
||||
import { AsideMenuContext, AsideMenuProvider } from './components/AsideMenuProvider';
|
||||
import { DesktopDrawer } from './components/DesktopDrawer';
|
||||
import { MenuItem } from './components/MenuItem';
|
||||
import { MobileDrawer } from './components/MobileDrawer';
|
||||
|
||||
import type { MenuItemProps } from './components/MenuItem/MenuItem.ui';
|
||||
import type { MenuItemProps } from './components/MenuItem/ui/MenuItem.ui';
|
||||
import type { BoxProps } from '@mui/material/Box/Box';
|
||||
import type {
|
||||
FC,
|
||||
ReactNode,
|
||||
DetailedHTMLProps,
|
||||
HTMLAttributes,
|
||||
} from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
const drawerWidth = 250;
|
||||
export const DESKTOP_DRAWER_WIDTH = 250;
|
||||
export const MOBILE_DRAWER_WIDTH = 250;
|
||||
|
||||
type AsideMenuLayoutProps = BoxProps & {
|
||||
export type AsideMenuLayoutProps = BoxProps & {
|
||||
pages: MenuItemProps['page'][];
|
||||
Content: ReactNode;
|
||||
Toolbar?: ReactNode;
|
||||
DrawerSwitcherProps?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
||||
};
|
||||
const AsideMenuLayout: FC<AsideMenuLayoutProps> = (props) => {
|
||||
const {
|
||||
pages,
|
||||
Content,
|
||||
Toolbar,
|
||||
DrawerSwitcherProps,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const { desktopDrawerOpen, setMobileDrawerOpen } = useContext(AsideMenuContext);
|
||||
const theme = useTheme();
|
||||
const isMobileView = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
const handleDrawerClose = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
const location = useLocation();
|
||||
const transitions = useTransition(location, {
|
||||
from: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
leave: { opacity: 0 },
|
||||
unique: true,
|
||||
reset: true,
|
||||
});
|
||||
|
||||
const AnimatedContent = (
|
||||
<>
|
||||
{transitions((styles) => (
|
||||
<animated.div style={styles}>
|
||||
{Content}
|
||||
</animated.div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
const AnimatedToolBar = (
|
||||
<>
|
||||
{transitions((styles) => (
|
||||
<animated.div style={styles}>
|
||||
{Toolbar}
|
||||
</animated.div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
if (isMobileView) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }} {...restProps}>
|
||||
{
|
||||
Boolean(Toolbar) && (
|
||||
<MobileAppBar position="static">
|
||||
<MobileDrawer width={MOBILE_DRAWER_WIDTH}>
|
||||
<List>
|
||||
{pages.map((page) => (
|
||||
<MenuItem
|
||||
key={page.path}
|
||||
page={page}
|
||||
onClick={() => { setMobileDrawerOpen(false); }}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</MobileDrawer>
|
||||
|
||||
{AnimatedToolBar}
|
||||
</MobileAppBar>
|
||||
)
|
||||
}
|
||||
|
||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||
{AnimatedContent}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }} {...restProps}>
|
||||
<CssBaseline />
|
||||
|
||||
<Drawer
|
||||
drawerWidth={drawerWidth}
|
||||
open={isOpen}
|
||||
variant="permanent"
|
||||
>
|
||||
<DrawerHeader>
|
||||
<DrawerSwitcher
|
||||
open={isOpen}
|
||||
onClick={isOpen ? handleDrawerClose : handleDrawerOpen}
|
||||
{...DrawerSwitcherProps}
|
||||
>
|
||||
{ isOpen ? <ChevronLeftIcon /> : <MenuIcon /> }
|
||||
</DrawerSwitcher>
|
||||
</DrawerHeader>
|
||||
|
||||
<Divider />
|
||||
|
||||
<DesktopDrawer width={DESKTOP_DRAWER_WIDTH}>
|
||||
<List>
|
||||
{pages.map((page) => (
|
||||
<MenuItem key={page.path} isOpen={isOpen} page={page} />
|
||||
))}
|
||||
desktopDrawerOpen
|
||||
? (
|
||||
<MenuItem key={page.path} isOpen={desktopDrawerOpen} page={page} />
|
||||
)
|
||||
: (
|
||||
<Tooltip key={page.path} placement="right" title={page.title}>
|
||||
<MenuItem isOpen={desktopDrawerOpen} page={page} />
|
||||
</Tooltip>
|
||||
)))}
|
||||
</List>
|
||||
</Drawer>
|
||||
</DesktopDrawer>
|
||||
|
||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
Toolbar
|
||||
? (
|
||||
<>
|
||||
<AppBar
|
||||
drawerWidth={drawerWidth}
|
||||
open={isOpen}
|
||||
position="fixed"
|
||||
>
|
||||
{Toolbar}
|
||||
</AppBar>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
{ Boolean(Toolbar) && (
|
||||
<DesktopAppBar position="static">
|
||||
{AnimatedToolBar}
|
||||
</DesktopAppBar>
|
||||
)}
|
||||
|
||||
<DrawerHeader />
|
||||
</>
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
{Content}
|
||||
<Box component="main" sx={{ p: 3 }}>
|
||||
{AnimatedContent}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AsideMenuLayout;
|
||||
const withProvider = (Component: FC<AsideMenuLayoutProps>) => (props: AsideMenuLayoutProps) => (
|
||||
<AsideMenuProvider>
|
||||
<Component {...props} />
|
||||
</AsideMenuProvider>
|
||||
);
|
||||
|
||||
export default withProvider(AsideMenuLayout);
|
||||
|
||||
@ -1,30 +1,21 @@
|
||||
import MuiAppBar from '@mui/material/AppBar';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
import type { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar/AppBar';
|
||||
|
||||
type AppBarProps = MuiAppBarProps & {
|
||||
open?: boolean;
|
||||
drawerWidth: number;
|
||||
};
|
||||
|
||||
const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => !['open', 'drawerWidth'].includes(prop as string) })<AppBarProps>(
|
||||
({ theme, open, drawerWidth }) => ({
|
||||
export const DesktopAppBar = styled(MuiAppBar)(
|
||||
({ theme }) => ({
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
marginLeft: `calc(${theme.spacing(7)} + 1px)`,
|
||||
width: `calc(100% - calc(${theme.spacing(7)} + 1px))`,
|
||||
...Boolean(open) && {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
}),
|
||||
);
|
||||
|
||||
export default AppBar;
|
||||
export const MobileAppBar = styled(MuiAppBar)(() => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: '0 24px',
|
||||
}));
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
export { default as AsideMenuProvider } from './ui/AsideMenuProvider.ui';
|
||||
export { AsideMenuContext } from './model/context';
|
||||
@ -0,0 +1,10 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export type AsideMenuContextType = {
|
||||
desktopDrawerOpen: boolean;
|
||||
setDesktopDrawerOpen: (isOpen: boolean) => void;
|
||||
mobileDrawerOpen: boolean;
|
||||
setMobileDrawerOpen: (isOpen: boolean) => void;
|
||||
};
|
||||
|
||||
export const AsideMenuContext = createContext<AsideMenuContextType>({} as AsideMenuContextType);
|
||||
@ -0,0 +1,31 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { AsideMenuContext, type AsideMenuContextType } from '../model/context';
|
||||
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
type AsideMenuProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
const AsideMenuProvider: FC<AsideMenuProviderProps> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
// todo desktop from default value localstorage
|
||||
const [desktopDrawerOpen, setDesktopDrawerOpen] = useState(true);
|
||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||
|
||||
const value = useMemo<AsideMenuContextType>(() => ({
|
||||
desktopDrawerOpen,
|
||||
setDesktopDrawerOpen,
|
||||
mobileDrawerOpen,
|
||||
setMobileDrawerOpen,
|
||||
}), [desktopDrawerOpen, mobileDrawerOpen]);
|
||||
|
||||
return (
|
||||
<AsideMenuContext.Provider value={value}>
|
||||
{children}
|
||||
</AsideMenuContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AsideMenuProvider;
|
||||
@ -0,0 +1,2 @@
|
||||
export type { DesktopDrawerProps } from './ui/DesktopDrawer.ui';
|
||||
export { default as DesktopDrawer } from './ui/DesktopDrawer.ui';
|
||||
@ -0,0 +1,53 @@
|
||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { AsideMenuContext } from '../../AsideMenuProvider';
|
||||
|
||||
import Drawer from './components/Drawer';
|
||||
import DrawerHeader from './components/DrawerMenu';
|
||||
import DrawerSwitcher from './components/DrawerSwitcher';
|
||||
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
export type DesktopDrawerProps = {
|
||||
width: number;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const DesktopDrawer: FC<DesktopDrawerProps> = (props) => {
|
||||
const {
|
||||
width,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
const { desktopDrawerOpen, setDesktopDrawerOpen } = useContext(AsideMenuContext);
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setDesktopDrawerOpen(true);
|
||||
};
|
||||
const handleDrawerClose = () => {
|
||||
setDesktopDrawerOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer open={desktopDrawerOpen} variant="permanent" width={width}>
|
||||
<DrawerHeader>
|
||||
<DrawerSwitcher
|
||||
data-testid="drawer-switcher"
|
||||
open={desktopDrawerOpen}
|
||||
onClick={desktopDrawerOpen ? handleDrawerClose : handleDrawerOpen}
|
||||
>
|
||||
{ desktopDrawerOpen ? <ChevronLeftIcon /> : <MenuIcon /> }
|
||||
</DrawerSwitcher>
|
||||
</DrawerHeader>
|
||||
|
||||
<Divider />
|
||||
|
||||
{children}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DesktopDrawer;
|
||||
@ -3,11 +3,11 @@ import { type CSSObject, styled, type Theme } from '@mui/material/styles';
|
||||
|
||||
type DrawerProps = {
|
||||
open: boolean;
|
||||
drawerWidth: number;
|
||||
width: number;
|
||||
};
|
||||
|
||||
const openedMixin = (theme: Theme, { drawerWidth }: Pick<DrawerProps, 'drawerWidth'>): CSSObject => ({
|
||||
width: drawerWidth,
|
||||
const openedMixin = (theme: Theme, { width }: Pick<DrawerProps, 'width'>): CSSObject => ({
|
||||
width,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
@ -24,15 +24,15 @@ const closedMixin = (theme: Theme): CSSObject => ({
|
||||
width: `calc(${theme.spacing(8)} + 1px)`,
|
||||
});
|
||||
|
||||
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => !['open', 'drawerWidth'].includes(prop as string) })<DrawerProps>(
|
||||
({ theme, open, drawerWidth }) => ({
|
||||
width: drawerWidth,
|
||||
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => !['open', 'width'].includes(prop as string) })<DrawerProps>(
|
||||
({ theme, open, width }) => ({
|
||||
width,
|
||||
flexShrink: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
boxSizing: 'border-box',
|
||||
...open && {
|
||||
...openedMixin(theme, { drawerWidth }),
|
||||
'& .MuiDrawer-paper': openedMixin(theme, { drawerWidth }),
|
||||
...openedMixin(theme, { width }),
|
||||
'& .MuiDrawer-paper': openedMixin(theme, { width }),
|
||||
},
|
||||
...!open && {
|
||||
...closedMixin(theme),
|
||||
@ -0,0 +1 @@
|
||||
export { default as MenuItem } from './ui/MenuItem.ui';
|
||||
@ -4,10 +4,11 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { forwardRef } from 'react';
|
||||
import { NavLink as RRDNavLink } from 'react-router-dom';
|
||||
|
||||
import type { ListItemProps } from '@mui/material/ListItem/ListItem';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { NavLinkProps } from 'react-router-dom';
|
||||
|
||||
const NavLink = styled(RRDNavLink)(({ theme }) => ({
|
||||
@ -26,7 +27,7 @@ export type MenuItemProps = ListItemProps & {
|
||||
NavLinkProps?: NavLinkProps;
|
||||
};
|
||||
|
||||
const MenuItem: FC<MenuItemProps> = (props) => {
|
||||
const MenuItem = forwardRef<HTMLLIElement, MenuItemProps>((props, ref) => {
|
||||
const {
|
||||
page,
|
||||
isOpen = true,
|
||||
@ -36,7 +37,7 @@ const MenuItem: FC<MenuItemProps> = (props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ListItem disablePadding {...restProps}>
|
||||
<ListItem ref={ref} disablePadding {...restProps}>
|
||||
<NavLink to={page.path} {...NavLinkProps}>
|
||||
{({ isActive }) => (
|
||||
<ListItemButton
|
||||
@ -72,6 +73,6 @@ const MenuItem: FC<MenuItemProps> = (props) => {
|
||||
</NavLink>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default MenuItem;
|
||||
@ -0,0 +1 @@
|
||||
export { default as MobileDrawer } from './ui/MobileDrawer.ui';
|
||||
@ -0,0 +1,56 @@
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import MuiDrawer from '@mui/material/Drawer';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { AsideMenuContext } from '../../AsideMenuProvider';
|
||||
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
type MobileDrawerProps = {
|
||||
children: ReactNode;
|
||||
width: number;
|
||||
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
const MobileDrawer: FC<MobileDrawerProps> = (props) => {
|
||||
const {
|
||||
children,
|
||||
width,
|
||||
onOpen,
|
||||
onClose,
|
||||
} = props;
|
||||
|
||||
const { mobileDrawerOpen, setMobileDrawerOpen } = useContext(AsideMenuContext);
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setMobileDrawerOpen(true);
|
||||
onOpen?.();
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setMobileDrawerOpen(false);
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleDrawerOpen}>
|
||||
<MenuIcon style={{ fill: '#fff' }} />
|
||||
</IconButton>
|
||||
|
||||
<MuiDrawer
|
||||
open={mobileDrawerOpen}
|
||||
sx={{ width }}
|
||||
onClose={handleDrawerClose}
|
||||
>
|
||||
{children}
|
||||
</MuiDrawer>
|
||||
</>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileDrawer;
|
||||
59
yarn.lock
59
yarn.lock
@ -4496,6 +4496,64 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-spring/animated@npm:~9.7.3":
|
||||
version: 9.7.3
|
||||
resolution: "@react-spring/animated@npm:9.7.3"
|
||||
dependencies:
|
||||
"@react-spring/shared": "npm:~9.7.3"
|
||||
"@react-spring/types": "npm:~9.7.3"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 10c0/5151da4fa7da010bb2edbee05871aa7a4aea8763fe617389d17605810aa0dd817374205e5fb3930b650f4a7f25fcdf23205fdfb7365686ff75888bdfd0b39839
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-spring/core@npm:~9.7.3":
|
||||
version: 9.7.3
|
||||
resolution: "@react-spring/core@npm:9.7.3"
|
||||
dependencies:
|
||||
"@react-spring/animated": "npm:~9.7.3"
|
||||
"@react-spring/shared": "npm:~9.7.3"
|
||||
"@react-spring/types": "npm:~9.7.3"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 10c0/e28c05de8435bf2eaf8481f8acdf093909d4be9881a9a854c51dfac7c2d5562088d0fb2ce04e2f07e1b3bf621d8da3ab57bf6fedb4fdc954e3aa263bc1e393af
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-spring/shared@npm:~9.7.3":
|
||||
version: 9.7.3
|
||||
resolution: "@react-spring/shared@npm:9.7.3"
|
||||
dependencies:
|
||||
"@react-spring/types": "npm:~9.7.3"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 10c0/afb03ed28ccf62efa4012e531c3659999bb364d1e0eb0fb729b962f9bd21a0b772a2d98e862062c9c32c06edf72327afcc45984d9eb22fdd961706b6ddf6950d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-spring/types@npm:~9.7.3":
|
||||
version: 9.7.3
|
||||
resolution: "@react-spring/types@npm:9.7.3"
|
||||
checksum: 10c0/d645044f3cc9ceb7c4f6c4d061aaf6660018568a1553d05638f56b3328c5f91597ee4118334abe22fc8f07f5ee02f054340170c1d52e11b3faea22888b5170d4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-spring/web@npm:9.7.3":
|
||||
version: 9.7.3
|
||||
resolution: "@react-spring/web@npm:9.7.3"
|
||||
dependencies:
|
||||
"@react-spring/animated": "npm:~9.7.3"
|
||||
"@react-spring/core": "npm:~9.7.3"
|
||||
"@react-spring/shared": "npm:~9.7.3"
|
||||
"@react-spring/types": "npm:~9.7.3"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 10c0/a5b4847a2921a29a3e8ce569f4951abeb268b6e8eb230f8c49d98709216b2b3a23ba1e58628c51c9eaa0bd20d83f9d7b8f8f03df98215e933de4c66c39e17fe1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@remix-run/router@npm:1.16.1":
|
||||
version: 1.16.1
|
||||
resolution: "@remix-run/router@npm:1.16.1"
|
||||
@ -12874,6 +12932,7 @@ __metadata:
|
||||
"@emotion/styled": "npm:11.11.5"
|
||||
"@mui/icons-material": "npm:^5.15.19"
|
||||
"@mui/material": "npm:5.15.19"
|
||||
"@react-spring/web": "npm:9.7.3"
|
||||
"@testing-library/jest-dom": "npm:6.4.2"
|
||||
"@testing-library/react": "npm:14.3.1"
|
||||
"@testing-library/user-event": "npm:^14.5.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user