1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Refactor app layouts and common components

This commit is contained in:
Bill Thornton 2023-09-27 02:07:40 -04:00
parent 6add573df6
commit 44678a61c2
22 changed files with 353 additions and 262 deletions

View file

@ -1,5 +1,7 @@
import React, { FC } from 'react';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes, useLocation } from 'react-router-dom';
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
@ -10,7 +12,7 @@ import LiveTvDrawerSection from './dashboard/LiveTvDrawerSection';
import PluginDrawerSection from './dashboard/PluginDrawerSection';
import ServerDrawerSection from './dashboard/ServerDrawerSection';
import MainDrawerContent from './MainDrawerContent';
import ResponsiveDrawer, { ResponsiveDrawerProps } from './ResponsiveDrawer';
import { isTabPath } from '../tabs/tabRoutes';
export const DRAWER_WIDTH = 240;
@ -36,6 +38,20 @@ export const isDrawerPath = (path: string) => (
|| 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> = ({
open = false,
onClose,
@ -48,13 +64,13 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
key={route.path}
path={route.path}
element={
<ResponsiveDrawer
<Drawer
open={open}
onClose={onClose}
onOpen={onOpen}
>
<MainDrawerContent />
</ResponsiveDrawer>
</Drawer>
}
/>
))
@ -65,7 +81,7 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
key={route.path}
path={route.path}
element={
<ResponsiveDrawer
<Drawer
open={open}
onClose={onClose}
onOpen={onOpen}
@ -75,7 +91,7 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
<LiveTvDrawerSection />
<AdvancedDrawerSection />
<PluginDrawerSection />
</ResponsiveDrawer>
</Drawer>
}
/>
))

View file

@ -1,45 +0,0 @@
import ListItemButton, { ListItemButtonBaseProps } from '@mui/material/ListItemButton';
import React, { FC } from 'react';
import { Link, useLocation, useSearchParams } from 'react-router-dom';
interface ListItemLinkProps extends ListItemButtonBaseProps {
to: string
}
const isMatchingParams = (routeParams: URLSearchParams, currentParams: URLSearchParams) => {
for (const param of routeParams) {
if (currentParams.get(param[0]) !== param[1]) {
return false;
}
}
return true;
};
const ListItemLink: FC<ListItemLinkProps> = ({
children,
to,
...params
}) => {
const location = useLocation();
const [ searchParams ] = useSearchParams();
const [ toPath, toParams ] = to.split('?');
// eslint-disable-next-line compat/compat
const toSearchParams = new URLSearchParams(`?${toParams}`);
const selected = location.pathname === toPath && (!toParams || isMatchingParams(toSearchParams, searchParams));
return (
<ListItemButton
component={Link}
to={to}
selected={selected}
{...params}
>
{children}
</ListItemButton>
);
};
export default ListItemLink;

View file

@ -17,12 +17,12 @@ import ListSubheader from '@mui/material/ListSubheader';
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import { appRouter } from 'components/router/appRouter';
import { useApi } from 'hooks/useApi';
import { useWebConfig } from 'hooks/useWebConfig';
import globalize from 'scripts/globalize';
import { appRouter } from 'components/router/appRouter';
import ListItemLink from './ListItemLink';
import LibraryIcon from '../LibraryIcon';
const MainDrawerContent = () => {

View file

@ -1,86 +0,0 @@
import { Theme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import SwipeableDrawer from '@mui/material/SwipeableDrawer';
import Toolbar from '@mui/material/Toolbar';
import useMediaQuery from '@mui/material/useMediaQuery';
import React, { FC, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import browser from 'scripts/browser';
import { DRAWER_WIDTH } from './AppDrawer';
import { isTabPath } from '../tabs/tabRoutes';
export interface ResponsiveDrawerProps {
open: boolean
onClose: () => void
onOpen: () => void
}
const ResponsiveDrawer: FC<ResponsiveDrawerProps> = ({
children,
open = false,
onClose,
onOpen
}) => {
const location = useLocation();
const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
const isLargeScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('lg'));
const isTallToolbar = isTabPath(location.pathname) && !isLargeScreen;
const getToolbarStyles = useCallback((theme: Theme) => ({
marginBottom: isTallToolbar ? theme.spacing(6) : 0
}), [ isTallToolbar ]);
return ( isSmallScreen ? (
/* DESKTOP DRAWER */
<Drawer
sx={{
width: DRAWER_WIDTH,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: DRAWER_WIDTH,
boxSizing: 'border-box'
}
}}
variant='persistent'
anchor='left'
open={open}
>
<Toolbar
variant='dense'
sx={getToolbarStyles}
/>
{children}
</Drawer>
) : (
/* MOBILE DRAWER */
<SwipeableDrawer
anchor='left'
open={open}
onClose={onClose}
onOpen={onOpen}
// Disable swipe to open on iOS since it interferes with back navigation
disableDiscovery={browser.iOS}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
<Toolbar
variant='dense'
sx={getToolbarStyles}
/>
<Box
role='presentation'
// Close the drawer when the content is clicked
onClick={onClose}
onKeyDown={onClose}
>
{children}
</Box>
</SwipeableDrawer>
));
};
export default ResponsiveDrawer;

View file

@ -15,10 +15,9 @@ import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import globalize from 'scripts/globalize';
import ListItemLink from '../ListItemLink';
const PLUGIN_PATHS = [
'/installedplugins.html',
'/availableplugins.html',

View file

@ -8,10 +8,9 @@ import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import globalize from 'scripts/globalize';
import ListItemLink from '../ListItemLink';
const DLNA_PATHS = [
'/dlnasettings.html',
'/dlnaprofiles.html'

View file

@ -6,10 +6,9 @@ import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import ListItemLink from 'components/ListItemLink';
import globalize from 'scripts/globalize';
import ListItemLink from '../ListItemLink';
const LiveTvDrawerSection = () => {
return (
<List

View file

@ -8,12 +8,11 @@ import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React, { useEffect, useState } from 'react';
import ListItemLink from 'components/ListItemLink';
import { useApi } from 'hooks/useApi';
import globalize from 'scripts/globalize';
import Dashboard from 'utils/dashboard';
import ListItemLink from '../ListItemLink';
const PluginDrawerSection = () => {
const { api } = useApi();
const [ pagesInfo, setPagesInfo ] = useState<ConfigurationPageInfo[]>([]);

View file

@ -8,10 +8,9 @@ import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import globalize from 'scripts/globalize';
import ListItemLink from '../ListItemLink';
const LIBRARY_PATHS = [
'/library.html',
'/librarydisplay.html',