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

Merge pull request #5020 from thornbill/mui-design-tweaks

Update mui based layout ui
This commit is contained in:
Bill Thornton 2024-01-09 12:46:17 -05:00 committed by GitHub
commit 6dc11f826f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 182 additions and 205 deletions

View file

@ -1,7 +1,8 @@
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import { useTheme } from '@mui/material/styles';
import React, { FC, useCallback, useEffect, useState } from 'react';
import { type Theme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import React, { FC, useCallback, useState } from 'react';
import { Outlet, useLocation } from 'react-router-dom';
import AppBody from 'components/AppBody';
@ -9,7 +10,6 @@ import AppToolbar from 'components/toolbar/AppToolbar';
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';
@ -19,34 +19,17 @@ interface AppLayoutProps {
drawerlessPaths: string[]
}
interface DashboardAppSettings {
isDrawerPinned: boolean
}
const DEFAULT_APP_SETTINGS: DashboardAppSettings = {
isDrawerPinned: false
};
const AppLayout: FC<AppLayoutProps> = ({
drawerlessPaths
}) => {
const [ appSettings, setAppSettings ] = useLocalStorage<DashboardAppSettings>('DashboardAppSettings', DEFAULT_APP_SETTINGS);
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
const location = useLocation();
const theme = useTheme();
const { user } = useApi();
const isDrawerAvailable = !drawerlessPaths.some(path => location.pathname.startsWith(`/${path}`));
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
useEffect(() => {
if (isDrawerActive !== appSettings.isDrawerPinned) {
setAppSettings({
...appSettings,
isDrawerPinned: isDrawerActive
});
}
}, [ appSettings, isDrawerActive, setAppSettings ]);
const isMediumScreen = useMediaQuery((t: Theme) => t.breakpoints.up('md'));
const isDrawerAvailable = Boolean(user)
&& !drawerlessPaths.some(path => location.pathname.startsWith(`/${path}`));
const isDrawerOpen = isDrawerActive && isDrawerAvailable;
const onToggleDrawer = useCallback(() => {
setIsDrawerActive(!isDrawerActive);
@ -54,47 +37,43 @@ const AppLayout: FC<AppLayoutProps> = ({
return (
<Box sx={{ display: 'flex' }}>
<ElevationScroll elevate={isDrawerOpen}>
<ElevationScroll elevate={false}>
<AppBar
position='fixed'
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
sx={{
width: {
xs: '100%',
md: isDrawerAvailable ? `calc(100% - ${DRAWER_WIDTH}px)` : '100%'
},
ml: {
xs: 0,
md: isDrawerAvailable ? DRAWER_WIDTH : 0
}
}}
>
<AppToolbar
isDrawerAvailable={isDrawerAvailable}
isDrawerAvailable={!isMediumScreen && isDrawerAvailable}
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
/>
</AppBar>
</ElevationScroll>
<AppDrawer
open={isDrawerOpen}
onClose={onToggleDrawer}
onOpen={onToggleDrawer}
/>
{
isDrawerAvailable && (
<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
})
flexGrow: 1
}}
>
<AppBody>

View file

@ -1,5 +1,8 @@
import ListItem from '@mui/material/ListItem';
import List from '@mui/material/List';
import React, { FC } from 'react';
import DrawerHeaderLink from 'apps/experimental/components/drawers/DrawerHeaderLink';
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
import ServerDrawerSection from './sections/ServerDrawerSection';
@ -18,6 +21,11 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
onClose={onClose}
onOpen={onOpen}
>
<List disablePadding>
<ListItem disablePadding>
<DrawerHeaderLink />
</ListItem>
</List>
<ServerDrawerSection />
<DevicesDrawerSection />
<LiveTvDrawerSection />

View file

@ -1,46 +1,28 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useState } from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import { useTheme } from '@mui/material/styles';
import { type Theme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import { Outlet, useLocation } from 'react-router-dom';
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 AppToolbar from './components/AppToolbar';
import AppDrawer, { isDrawerPath } from './components/drawers/AppDrawer';
import './AppOverrides.scss';
interface ExperimentalAppSettings {
isDrawerPinned: boolean
}
const DEFAULT_EXPERIMENTAL_APP_SETTINGS: ExperimentalAppSettings = {
isDrawerPinned: false
};
const AppLayout = () => {
const [ appSettings, setAppSettings ] = useLocalStorage<ExperimentalAppSettings>('ExperimentalAppSettings', DEFAULT_EXPERIMENTAL_APP_SETTINGS);
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
const { user } = useApi();
const location = useLocation();
const theme = useTheme();
const isDrawerAvailable = isDrawerPath(location.pathname);
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
useEffect(() => {
if (isDrawerActive !== appSettings.isDrawerPinned) {
setAppSettings({
...appSettings,
isDrawerPinned: isDrawerActive
});
}
}, [ appSettings, isDrawerActive, setAppSettings ]);
const isMediumScreen = useMediaQuery((t: Theme) => t.breakpoints.up('md'));
const isDrawerAvailable = isDrawerPath(location.pathname) && Boolean(user);
const isDrawerOpen = isDrawerActive && isDrawerAvailable;
const onToggleDrawer = useCallback(() => {
setIsDrawerActive(!isDrawerActive);
@ -48,46 +30,43 @@ const AppLayout = () => {
return (
<Box sx={{ display: 'flex' }}>
<ElevationScroll elevate={isDrawerOpen}>
<ElevationScroll elevate={false}>
<AppBar
position='fixed'
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
sx={{
width: {
xs: '100%',
md: isDrawerAvailable ? `calc(100% - ${DRAWER_WIDTH}px)` : '100%'
},
ml: {
xs: 0,
md: isDrawerAvailable ? DRAWER_WIDTH : 0
}
}}
>
<AppToolbar
isDrawerAvailable={!isMediumScreen && isDrawerAvailable}
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
/>
</AppBar>
</ElevationScroll>
<AppDrawer
open={isDrawerOpen}
onClose={onToggleDrawer}
onOpen={onToggleDrawer}
/>
{
isDrawerAvailable && (
<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
})
flexGrow: 1
}}
>
<AppBody>

View file

@ -20,11 +20,15 @@ $mui-bp-xl: 1536px;
}
// Fix the padding of some pages
.homePage.libraryPage, // Home page
.libraryPage:not(.withTabs) { // Tabless library pages
.homePage.libraryPage.withTabs, // Home page
// Library pages excluding the item details page and tabbed pages
.libraryPage:not(
.itemDetailPage,
.withTabs
) {
padding-top: 3.25rem !important;
}
// Tabbed library pages
.libraryPage.withTabs {
padding-top: 6.5rem !important;

View file

@ -8,22 +8,22 @@ import AppToolbar from 'components/toolbar/AppToolbar';
import globalize from 'scripts/globalize';
import AppTabs from '../tabs/AppTabs';
import { isDrawerPath } from '../drawers/AppDrawer';
import RemotePlayButton from './RemotePlayButton';
import SyncPlayButton from './SyncPlayButton';
import { isTabPath } from '../tabs/tabRoutes';
interface AppToolbarProps {
isDrawerAvailable: boolean
isDrawerOpen: boolean
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void
}
const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
isDrawerAvailable,
isDrawerOpen,
onDrawerButtonClick
}) => {
const location = useLocation();
const isDrawerAvailable = isDrawerPath(location.pathname);
const isTabsAvailable = isTabPath(location.pathname);
return (

View file

@ -1,11 +1,9 @@
import React, { FC } from 'react';
import { useLocation } from 'react-router-dom';
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
import { isTabPath } from '../tabs/tabRoutes';
import MainDrawerContent from './MainDrawerContent';
@ -27,20 +25,14 @@ const AppDrawer: FC<ResponsiveDrawerProps> = ({
open = false,
onClose,
onOpen
}) => {
const location = useLocation();
const hasSecondaryToolBar = isTabPath(location.pathname);
return (
<ResponsiveDrawer
hasSecondaryToolBar={hasSecondaryToolBar}
open={open}
onClose={onClose}
onOpen={onOpen}
>
<MainDrawerContent />
</ResponsiveDrawer>
);
};
}) => (
<ResponsiveDrawer
open={open}
onClose={onClose}
onOpen={onOpen}
>
<MainDrawerContent />
</ResponsiveDrawer>
);
export default AppDrawer;

View file

@ -0,0 +1,33 @@
import Box from '@mui/material/Box';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import React from 'react';
import { useApi } from 'hooks/useApi';
import { useSystemInfo } from 'hooks/useSystemInfo';
import ListItemLink from 'components/ListItemLink';
import appIcon from 'assets/img/icon-transparent.png';
const DrawerHeaderLink = () => {
const { api } = useApi();
const { data: systemInfo } = useSystemInfo(api);
return (
<ListItemLink to='/'>
<ListItemIcon sx={{ minWidth: 56 }}>
<Box
component='img'
src={appIcon}
sx={{ height: '2.5rem' }}
/>
</ListItemIcon>
<ListItemText
primary={systemInfo?.ServerName || 'Jellyfin'}
primaryTypographyProps={{ variant: 'h6' }}
secondary={systemInfo?.Version}
/>
</ListItemLink>);
};
export default DrawerHeaderLink;

View file

@ -1,7 +1,5 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
import type { SystemInfo } from '@jellyfin/sdk/lib/generated-client/models/system-info';
import { getUserViewsApi } from '@jellyfin/sdk/lib/utils/api/user-views-api';
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
import Dashboard from '@mui/icons-material/Dashboard';
import Edit from '@mui/icons-material/Edit';
import Favorite from '@mui/icons-material/Favorite';
@ -24,11 +22,11 @@ import { useWebConfig } from 'hooks/useWebConfig';
import globalize from 'scripts/globalize';
import LibraryIcon from '../LibraryIcon';
import DrawerHeaderLink from './DrawerHeaderLink';
const MainDrawerContent = () => {
const { api, user } = useApi();
const location = useLocation();
const [ systemInfo, setSystemInfo ] = useState<SystemInfo>();
const [ userViews, setUserViews ] = useState<BaseItemDto[]>([]);
const webConfig = useWebConfig();
@ -45,15 +43,6 @@ const MainDrawerContent = () => {
console.warn('[MainDrawer] failed to fetch user views', err);
setUserViews([]);
});
getSystemApi(api)
.getSystemInfo()
.then(({ data }) => {
setSystemInfo(data);
})
.catch(err => {
console.warn('[MainDrawer] failed to fetch system info', err);
});
} else {
setUserViews([]);
}
@ -62,7 +51,10 @@ const MainDrawerContent = () => {
return (
<>
{/* MAIN LINKS */}
<List>
<List sx={{ paddingTop: 0 }}>
<ListItem disablePadding>
<DrawerHeaderLink />
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/home.html' selected={isHomeSelected}>
<ListItemIcon>
@ -167,17 +159,6 @@ const MainDrawerContent = () => {
</List>
</>
)}
{/* FOOTER */}
<Divider style={{ marginTop: 'auto' }} />
<List>
<ListItem>
<ListItemText
primary={systemInfo?.ServerName ? systemInfo.ServerName : 'Jellyfin'}
secondary={systemInfo?.Version ? `v${systemInfo.Version}` : ''}
/>
</ListItem>
</List>
</>
);
};