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

Backport pull request #5529 from jellyfin-web/release-10.9.z

Restore library menu tabs functionality

Original-merge: 7ce8c070b3

Merged-by: thornbill <thornbill@users.noreply.github.com>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
This commit is contained in:
thornbill 2024-05-17 13:52:49 -04:00 committed by Joshua M. Boniface
parent 5b1a65112a
commit e2f805da19
26 changed files with 124 additions and 209 deletions

View file

@ -11,6 +11,7 @@ import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
import { useApi } from 'hooks/useApi';
import AppTabs from './components/AppTabs';
import AppDrawer from './components/drawer/AppDrawer';
import './AppOverrides.scss';
@ -55,7 +56,9 @@ const AppLayout: FC<AppLayoutProps> = ({
isDrawerAvailable={!isMediumScreen && isDrawerAvailable}
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
/>
>
<AppTabs isDrawerOpen={isDrawerOpen} />
</AppToolbar>
</AppBar>
</ElevationScroll>

View file

@ -16,7 +16,15 @@ $mui-bp-xl: 1536px;
}
// Fix the padding of dashboard pages
.content-primary.content-primary {
padding-top: 3.25rem !important;
.content-primary {
padding-top: 3.25rem;
}
// Tabbed pages
.withTabs .content-primary {
padding-top: 6.5rem;
@media all and (min-width: $mui-bp-lg) {
padding-top: 3.25rem;
}
}
}

View file

@ -0,0 +1,96 @@
import { Theme } from '@mui/material/styles';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import useMediaQuery from '@mui/material/useMediaQuery';
import debounce from 'lodash-es/debounce';
import isEqual from 'lodash-es/isEqual';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { EventType } from 'types/eventType';
import Events, { type Event } from 'utils/events';
interface AppTabsParams {
isDrawerOpen: boolean
}
interface TabDefinition {
href: string
name: string
}
const handleResize = debounce(() => window.dispatchEvent(new Event('resize')), 100);
const AppTabs: FC<AppTabsParams> = ({
isDrawerOpen
}) => {
const documentRef = useRef<Document>(document);
const [ activeIndex, setActiveIndex ] = useState(0);
const [ tabs, setTabs ] = useState<TabDefinition[]>();
const isBigScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
const onTabsUpdate = useCallback((
_e: Event,
_newView?: string,
newIndex: number | undefined = 0,
newTabs?: TabDefinition[]
) => {
setActiveIndex(newIndex);
if (!isEqual(tabs, newTabs)) {
setTabs(newTabs);
}
}, [ tabs ]);
useEffect(() => {
const doc = documentRef.current;
if (doc) Events.on(doc, EventType.SET_TABS, onTabsUpdate);
return () => {
if (doc) Events.off(doc, EventType.SET_TABS, onTabsUpdate);
};
}, [ onTabsUpdate ]);
// HACK: Force resizing to workaround upstream bug with tab resizing
// https://github.com/mui/material-ui/issues/24011
useEffect(() => {
handleResize();
}, [ isDrawerOpen ]);
if (!tabs?.length) return null;
return (
<Tabs
value={activeIndex}
sx={{
width: '100%',
flexShrink: {
xs: 0,
lg: 'unset'
},
order: {
xs: 100,
lg: 'unset'
}
}}
variant={isBigScreen ? 'standard' : 'scrollable'}
centered={isBigScreen}
>
{
tabs.map(({ href, name }, index) => (
<Tab
key={`tab-${name}`}
label={name}
data-tab-index={`${index}`}
component={Link}
to={href}
/>
))
}
</Tabs>
);
};
export default AppTabs;

View file

@ -1,4 +1,4 @@
<div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
<div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div>
<div class="content-primary">
<form class="encodingSettingsForm">

View file

@ -2,7 +2,6 @@ import 'jquery';
import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom';
import libraryMenu from '../../scripts/libraryMenu';
import Dashboard from '../../utils/dashboard';
import alert from '../../components/alert';
@ -167,22 +166,6 @@ function setDecodingCodecsVisible(context, value) {
}
}
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
let systemInfo;
function getSystemInfo() {
return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then(
@ -292,7 +275,6 @@ $(document).on('pageinit', '#encodingSettingsPage', function () {
$('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#encodingSettingsPage', function () {
loading.show();
libraryMenu.setTabs('playback', 0, getTabs);
const page = this;
ApiClient.getNamedConfiguration('encoding').then(function (config) {
ApiClient.getSystemInfo().then(function (fetchedSystemInfo) {

View file

@ -1,4 +1,4 @@
<div id="mediaLibraryPage" data-role="page" class="page type-interior mediaLibraryPage librarySectionPage withTabs fullWidthContent">
<div id="mediaLibraryPage" data-role="page" class="page type-interior mediaLibraryPage librarySectionPage fullWidthContent">
<div>
<div class="content-primary">
<div class="padded-top padded-bottom">

View file

@ -2,7 +2,6 @@ import escapeHtml from 'escape-html';
import 'jquery';
import taskButton from '../../scripts/taskbutton';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom';
import imageHelper from '../../utils/image';
@ -358,22 +357,6 @@ function getVirtualFolderHtml(page, virtualFolder, index) {
return html;
}
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
window.WizardLibraryPage = {
next: function () {
Dashboard.navigate('wizardsettings.html');
@ -383,8 +366,6 @@ pageClassOn('pageshow', 'mediaLibraryPage', function () {
reloadLibrary(this);
});
pageIdOn('pageshow', 'mediaLibraryPage', function () {
libraryMenu.setTabs('librarysetup', 0, getTabs);
const page = this;
taskButton({
mode: 'on',

View file

@ -1,4 +1,4 @@
<div id="libraryDisplayPage" data-role="page" class="page type-interior librarySectionPage withTabs">
<div id="libraryDisplayPage" data-role="page" class="page type-interior librarySectionPage">
<div>
<div class="content-primary">
<form>

View file

@ -1,26 +1,8 @@
import globalize from '../../scripts/globalize';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import '../../elements/emby-checkbox/emby-checkbox';
import '../../elements/emby-button/emby-button';
import Dashboard from '../../utils/dashboard';
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
export default function(view) {
function loadData() {
ApiClient.getServerConfiguration().then(function(config) {
@ -57,7 +39,6 @@ export default function(view) {
});
view.addEventListener('viewshow', function() {
libraryMenu.setTabs('librarysetup', 1, getTabs);
loadData();
ApiClient.getSystemInfo().then(function(info) {
if (info.OperatingSystem === 'Windows') {

View file

@ -3,7 +3,6 @@ import { ImageResolution } from '@jellyfin/sdk/lib/generated-client/models/image
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
@ -86,26 +85,9 @@ function onSubmit() {
return false;
}
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
$(document).on('pageinit', '#metadataImagesConfigurationPage', function() {
$('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataImagesConfigurationPage', function() {
libraryMenu.setTabs('metadata', 2, getTabs);
loading.show();
loadPage(this);
});

View file

@ -1,4 +1,4 @@
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs">
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage">
<div>

View file

@ -1,4 +1,4 @@
<div id="metadataNfoPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs">
<div id="metadataNfoPage" data-role="page" class="page type-interior metadataConfigurationPage">
<div>

View file

@ -1,7 +1,6 @@
import escapeHtml from 'escape-html';
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
import alert from '../../components/alert';
@ -44,27 +43,10 @@ function showConfirmMessage() {
});
}
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
const metadataKey = 'xbmcmetadata';
$(document).on('pageinit', '#metadataNfoPage', function () {
$('.metadataNfoForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataNfoPage', function () {
libraryMenu.setTabs('metadata', 3, getTabs);
loading.show();
const page = this;
const promise1 = ApiClient.getUsers();

View file

@ -1,4 +1,4 @@
<div id="playbackConfigurationPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
<div id="playbackConfigurationPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div>
<div class="content-primary">
<form class="playbackConfigurationForm">

View file

@ -1,7 +1,5 @@
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
function loadPage(page, config) {
@ -29,27 +27,10 @@ function onSubmit() {
return false;
}
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
$(document).on('pageinit', '#playbackConfigurationPage', function () {
$('.playbackConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#playbackConfigurationPage', function () {
loading.show();
libraryMenu.setTabs('playback', 1, getTabs);
const page = this;
ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config);

View file

@ -1,4 +1,4 @@
<div id="pluginCatalogPage" data-role="page" class="page type-interior pluginConfigurationPage withTabs fullWidthContent">
<div id="pluginCatalogPage" data-role="page" class="page type-interior pluginConfigurationPage fullWidthContent">
<div>
<div class="content-primary">
<div class="inputContainer">

View file

@ -1,7 +1,6 @@
import escapeHTML from 'escape-html';
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize';
import '../../../../components/cardbuilder/card.scss';
import '../../../../elements/emby-button/emby-button';
@ -159,22 +158,8 @@ function getPluginHtml(plugin, options, installedPlugins) {
return html;
}
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
export default function (view) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 1, getTabs);
reloadList(this);
});
}

View file

@ -1,4 +1,4 @@
<div id="pluginsPage" data-role="page" class="page type-interior pluginConfigurationPage withTabs fullWidthContent">
<div id="pluginsPage" data-role="page" class="page type-interior pluginConfigurationPage fullWidthContent">
<div>
<div class="content-primary">
<div class="inputContainer">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import dom from '../../../../scripts/dom';
import globalize from '../../../../scripts/globalize';
import '../../../../components/cardbuilder/card.scss';
@ -219,19 +218,6 @@ function reloadList(page) {
});
}
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
function onInstalledPluginsClick(e) {
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
showNoConfigurationMessage();
@ -257,7 +243,6 @@ function onFilterType(page, searchBar) {
}
pageIdOn('pageshow', 'pluginsPage', function () {
libraryMenu.setTabs('plugins', 0, getTabs);
reloadList(this);
});

View file

@ -1,4 +1,4 @@
<div id="repositories" data-role="page" class="page type-interior withTabs fullWidthContent">
<div id="repositories" data-role="page" class="page type-interior fullWidthContent">
<div>
<div class="content-primary">
<div class="sectionTitleContainer flex align-items-center">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize';
import dialogHelper from '../../../../components/dialogHelper/dialogHelper';
import confirm from '../../../../components/confirm/confirm';
@ -103,22 +102,8 @@ function getRepositoryElement(repository) {
return listItem;
}
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
export default function(view) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this);
const save = this;

View file

@ -1,4 +1,4 @@
<div id="streamingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs">
<div id="streamingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div>
<div class="content-primary">
<form class="streamingSettingsForm">

View file

@ -1,7 +1,5 @@
import 'jquery';
import libraryMenu from '../../scripts/libraryMenu';
import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
function loadPage(page, config) {
@ -20,27 +18,10 @@ function onSubmit() {
return false;
}
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
$(document).on('pageinit', '#streamingSettingsPage', function () {
$('.streamingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#streamingSettingsPage', function () {
loading.show();
libraryMenu.setTabs('playback', 2, getTabs);
const page = this;
ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config);

View file

@ -30,6 +30,7 @@ import '../elements/emby-button/paper-icon-button-light';
import 'material-design-icons-iconfont';
import '../styles/scrollstyles.scss';
import '../styles/flexstyles.scss';
import { EventType } from 'types/eventType';
function renderHeader() {
let html = '';
@ -703,6 +704,8 @@ const skinHeader = document.querySelector('.skinHeader');
let requiresUserRefresh = true;
function setTabs (type, selectedIndex, builder) {
Events.trigger(document, EventType.SET_TABS, type ? [ type, selectedIndex, builder()] : []);
import('../components/maintabsmanager').then((mainTabsManager) => {
if (type) {
mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () {

View file

@ -152,31 +152,10 @@ div[data-role=controlgroup] a.ui-btn-active {
}
.content-primary {
padding-top: 6em;
padding-right: 1em;
padding-left: 1em;
}
.withTabs .content-primary {
padding-top: 9em !important;
}
@media all and (min-width: 40em) {
.content-primary {
padding-top: 4.6em;
}
.withTabs .content-primary {
padding-top: 10em !important;
}
}
@media all and (min-width: 84em) {
.withTabs .content-primary {
padding-top: 7em !important;
}
}
.content-primary ul:first-child {
margin-top: 0;
}

View file

@ -2,5 +2,6 @@
* Custom event types.
*/
export enum EventType {
SET_TABS = 'SET_TABS',
SHOW_VIDEO_OSD = 'SHOW_VIDEO_OSD'
}