mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Refactor common navigation components
This commit is contained in:
parent
06386a8eb6
commit
1a934c7956
8 changed files with 138 additions and 75 deletions
|
@ -1,15 +1,103 @@
|
||||||
import React from 'react';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import { Outlet } from 'react-router-dom';
|
import Box from '@mui/material/Box';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import AppBody from 'components/AppBody';
|
import AppBody from 'components/AppBody';
|
||||||
|
import ElevationScroll from 'components/ElevationScroll';
|
||||||
|
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
||||||
|
import { useApi } from 'hooks/useApi';
|
||||||
|
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||||
|
|
||||||
|
import AppDrawer from './components/drawer/AppDrawer';
|
||||||
|
|
||||||
|
// FIXME: Remove main app override styles
|
||||||
import '../experimental/AppOverrides.scss';
|
import '../experimental/AppOverrides.scss';
|
||||||
|
import AppToolbar from 'components/toolbar/AppToolbar';
|
||||||
|
|
||||||
|
interface DashboardAppSettings {
|
||||||
|
isDrawerPinned: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_APP_SETTINGS: DashboardAppSettings = {
|
||||||
|
isDrawerPinned: false
|
||||||
|
};
|
||||||
|
|
||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
|
const [ appSettings, setAppSettings ] = useLocalStorage<DashboardAppSettings>('DashboardAppSettings', DEFAULT_APP_SETTINGS);
|
||||||
|
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
|
||||||
|
const location = useLocation();
|
||||||
|
const theme = useTheme();
|
||||||
|
const { user } = useApi();
|
||||||
|
|
||||||
|
// FIXME: Use const for metadata editor
|
||||||
|
const isDrawerAvailable = !location.pathname.startsWith('/metadata');
|
||||||
|
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDrawerActive !== appSettings.isDrawerPinned) {
|
||||||
|
setAppSettings({
|
||||||
|
...appSettings,
|
||||||
|
isDrawerPinned: isDrawerActive
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [ appSettings, isDrawerActive, setAppSettings ]);
|
||||||
|
|
||||||
|
const onToggleDrawer = useCallback(() => {
|
||||||
|
setIsDrawerActive(!isDrawerActive);
|
||||||
|
}, [ isDrawerActive, setIsDrawerActive ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBody>
|
<Box sx={{ display: 'flex' }}>
|
||||||
<Outlet />
|
<ElevationScroll elevate={isDrawerOpen}>
|
||||||
</AppBody>
|
<AppBar
|
||||||
|
position='fixed'
|
||||||
|
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
|
||||||
|
>
|
||||||
|
<AppToolbar
|
||||||
|
isDrawerAvailable={isDrawerAvailable}
|
||||||
|
isDrawerOpen={isDrawerOpen}
|
||||||
|
onDrawerButtonClick={onToggleDrawer}
|
||||||
|
/>
|
||||||
|
</AppBar>
|
||||||
|
</ElevationScroll>
|
||||||
|
|
||||||
|
<AppDrawer
|
||||||
|
open={isDrawerOpen}
|
||||||
|
onClose={onToggleDrawer}
|
||||||
|
onOpen={onToggleDrawer}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
component='main'
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
flexGrow: 1,
|
||||||
|
transition: theme.transitions.create('margin', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen
|
||||||
|
}),
|
||||||
|
marginLeft: 0,
|
||||||
|
...(isDrawerAvailable && {
|
||||||
|
marginLeft: {
|
||||||
|
sm: `-${DRAWER_WIDTH}px`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
...(isDrawerActive && {
|
||||||
|
transition: theme.transitions.create('margin', {
|
||||||
|
easing: theme.transitions.easing.easeOut,
|
||||||
|
duration: theme.transitions.duration.enteringScreen
|
||||||
|
}),
|
||||||
|
marginLeft: 0
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppBody>
|
||||||
|
<Outlet />
|
||||||
|
</AppBody>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
29
src/apps/dashboard/components/drawer/AppDrawer.tsx
Normal file
29
src/apps/dashboard/components/drawer/AppDrawer.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||||
|
|
||||||
|
import ServerDrawerSection from './sections/ServerDrawerSection';
|
||||||
|
import DevicesDrawerSection from './sections/DevicesDrawerSection';
|
||||||
|
import LiveTvDrawerSection from './sections/LiveTvDrawerSection';
|
||||||
|
import AdvancedDrawerSection from './sections/AdvancedDrawerSection';
|
||||||
|
import PluginDrawerSection from './sections/PluginDrawerSection';
|
||||||
|
|
||||||
|
const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
|
open = false,
|
||||||
|
onClose,
|
||||||
|
onOpen
|
||||||
|
}) => (
|
||||||
|
<ResponsiveDrawer
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
onOpen={onOpen}
|
||||||
|
>
|
||||||
|
<ServerDrawerSection />
|
||||||
|
<DevicesDrawerSection />
|
||||||
|
<LiveTvDrawerSection />
|
||||||
|
<AdvancedDrawerSection />
|
||||||
|
<PluginDrawerSection />
|
||||||
|
</ResponsiveDrawer>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default AppDrawer;
|
|
@ -1,23 +1,15 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||||
|
|
||||||
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
||||||
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
||||||
|
|
||||||
import AdvancedDrawerSection from './dashboard/AdvancedDrawerSection';
|
|
||||||
import DevicesDrawerSection from './dashboard/DevicesDrawerSection';
|
|
||||||
import LiveTvDrawerSection from './dashboard/LiveTvDrawerSection';
|
|
||||||
import PluginDrawerSection from './dashboard/PluginDrawerSection';
|
|
||||||
import ServerDrawerSection from './dashboard/ServerDrawerSection';
|
|
||||||
import MainDrawerContent from './MainDrawerContent';
|
import MainDrawerContent from './MainDrawerContent';
|
||||||
import { isTabPath } from '../tabs/tabRoutes';
|
import { isTabPath } from '../tabs/tabRoutes';
|
||||||
|
|
||||||
export const DRAWER_WIDTH = 240;
|
|
||||||
|
|
||||||
const DRAWERLESS_ROUTES = [
|
const DRAWERLESS_ROUTES = [
|
||||||
'metadata', // metadata manager
|
|
||||||
'video' // video player
|
'video' // video player
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -26,75 +18,29 @@ const MAIN_DRAWER_ROUTES = [
|
||||||
...LEGACY_USER_ROUTES
|
...LEGACY_USER_ROUTES
|
||||||
].filter(route => !DRAWERLESS_ROUTES.includes(route.path));
|
].filter(route => !DRAWERLESS_ROUTES.includes(route.path));
|
||||||
|
|
||||||
const ADMIN_DRAWER_ROUTES = [
|
|
||||||
{ path: '/configurationpage' } // Plugin configuration page
|
|
||||||
].filter(route => !DRAWERLESS_ROUTES.includes(route.path));
|
|
||||||
|
|
||||||
/** Utility function to check if a path has a drawer. */
|
/** Utility function to check if a path has a drawer. */
|
||||||
export const isDrawerPath = (path: string) => (
|
export const isDrawerPath = (path: string) => (
|
||||||
MAIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path)
|
MAIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path)
|
||||||
|| ADMIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const Drawer: FC<ResponsiveDrawerProps> = ({ children, ...props }) => {
|
|
||||||
const location = useLocation();
|
|
||||||
const hasSecondaryToolBar = isTabPath(location.pathname);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ResponsiveDrawer
|
|
||||||
{...props}
|
|
||||||
hasSecondaryToolBar={hasSecondaryToolBar}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ResponsiveDrawer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||||
open = false,
|
open = false,
|
||||||
onClose,
|
onClose,
|
||||||
onOpen
|
onOpen
|
||||||
}) => (
|
}) => {
|
||||||
<Routes>
|
const location = useLocation();
|
||||||
{
|
const hasSecondaryToolBar = isTabPath(location.pathname);
|
||||||
MAIN_DRAWER_ROUTES.map(route => (
|
|
||||||
<Route
|
return (
|
||||||
key={route.path}
|
<ResponsiveDrawer
|
||||||
path={route.path}
|
hasSecondaryToolBar={hasSecondaryToolBar}
|
||||||
element={
|
open={open}
|
||||||
<Drawer
|
onClose={onClose}
|
||||||
open={open}
|
onOpen={onOpen}
|
||||||
onClose={onClose}
|
>
|
||||||
onOpen={onOpen}
|
<MainDrawerContent />
|
||||||
>
|
</ResponsiveDrawer>
|
||||||
<MainDrawerContent />
|
);
|
||||||
</Drawer>
|
};
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ADMIN_DRAWER_ROUTES.map(route => (
|
|
||||||
<Route
|
|
||||||
key={route.path}
|
|
||||||
path={route.path}
|
|
||||||
element={
|
|
||||||
<Drawer
|
|
||||||
open={open}
|
|
||||||
onClose={onClose}
|
|
||||||
onOpen={onOpen}
|
|
||||||
>
|
|
||||||
<ServerDrawerSection />
|
|
||||||
<DevicesDrawerSection />
|
|
||||||
<LiveTvDrawerSection />
|
|
||||||
<AdvancedDrawerSection />
|
|
||||||
<PluginDrawerSection />
|
|
||||||
</Drawer>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Routes>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default AppDrawer;
|
export default AppDrawer;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue