From cc19ae9ca9863aa3e63437cf3b2a36e6503e82b1 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 16 Apr 2025 14:09:21 -0400 Subject: [PATCH] Update experimental layout library controls --- .../components/library/GridListViewButton.tsx | 64 ------ .../components/library/ItemsView.tsx | 178 +++++++++------ .../library/NewCollectionButton.tsx | 15 +- .../components/library/Pagination.tsx | 73 +++---- .../components/library/PlayAllButton.tsx | 18 +- .../components/library/QueueButton.tsx | 34 +-- .../components/library/ShuffleButton.tsx | 38 ++-- .../components/library/SortButton.tsx | 13 +- .../components/library/ViewSettingsButton.tsx | 205 +++++++++++------- .../library/filter/FilterButton.tsx | 17 +- src/strings/en-us.json | 1 + 11 files changed, 342 insertions(+), 314 deletions(-) delete mode 100644 src/apps/experimental/components/library/GridListViewButton.tsx diff --git a/src/apps/experimental/components/library/GridListViewButton.tsx b/src/apps/experimental/components/library/GridListViewButton.tsx deleted file mode 100644 index bf6f48391..000000000 --- a/src/apps/experimental/components/library/GridListViewButton.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { FC, useCallback } from 'react'; -import ButtonGroup from '@mui/material/ButtonGroup/ButtonGroup'; -import IconButton from '@mui/material/IconButton'; -import ViewModuleIcon from '@mui/icons-material/ViewModule'; -import ViewListIcon from '@mui/icons-material/ViewList'; - -import globalize from 'lib/globalize'; -import { LibraryViewSettings, ViewMode } from 'types/library'; -import { LibraryTab } from 'types/libraryTab'; -import ViewSettingsButton from './ViewSettingsButton'; - -interface GridListViewButtonProps { - viewType: LibraryTab; - libraryViewSettings: LibraryViewSettings; - setLibraryViewSettings: React.Dispatch>; -} - -const GridListViewButton: FC = ({ - viewType, - libraryViewSettings, - setLibraryViewSettings -}) => { - const handleToggleCurrentView = useCallback(() => { - setLibraryViewSettings((prevState) => ({ - ...prevState, - ViewMode: - prevState.ViewMode === ViewMode.ListView ? ViewMode.GridView : ViewMode.ListView - })); - }, [setLibraryViewSettings]); - - const isGridView = libraryViewSettings.ViewMode === ViewMode.GridView; - - return ( - - {isGridView ? ( - - ) : ( - - - - )} - - - - - - ); -}; - -export default GridListViewButton; diff --git a/src/apps/experimental/components/library/ItemsView.tsx b/src/apps/experimental/components/library/ItemsView.tsx index aa6e8f881..c6d43d625 100644 --- a/src/apps/experimental/components/library/ItemsView.tsx +++ b/src/apps/experimental/components/library/ItemsView.tsx @@ -2,9 +2,10 @@ import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/bas import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type'; import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'; import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by'; -import React, { type FC, useCallback } from 'react'; import Box from '@mui/material/Box'; +import ButtonGroup from '@mui/material/ButtonGroup'; import classNames from 'classnames'; +import React, { type FC, useCallback } from 'react'; import { useApi } from 'hooks/useApi'; import { useLocalStorage } from 'hooks/useLocalStorage'; @@ -31,8 +32,8 @@ import PlayAllButton from './PlayAllButton'; import QueueButton from './QueueButton'; import ShuffleButton from './ShuffleButton'; import SortButton from './SortButton'; -import GridListViewButton from './GridListViewButton'; import LibraryViewMenu from './LibraryViewMenu'; +import ViewSettingsButton from './ViewSettingsButton'; interface ItemsViewProps { viewType: LibraryTab; @@ -237,82 +238,127 @@ const ItemsView: FC = ({ )} sx={{ display: 'flex', - flexWrap: 'wrap' + flexWrap: 'wrap', + alignItems: 'center' }} > + + + - - - {isBtnPlayAllEnabled && ( - - )} - {isBtnShuffleEnabled && totalRecordCount > 1 && ( - - )} - {isBtnQueueEnabled - && item - && playbackManager.canQueue(item) && ( - - )} - {isBtnSortEnabled && ( - - )} - {isBtnFilterEnabled && ( - - )} - {isBtnNewCollectionEnabled && } - {isBtnGridListEnabled && ( - - )} - - {isPaginationEnabled && ( - + + {isBtnPlayAllEnabled && ( + + )} + + {isBtnShuffleEnabled && totalRecordCount > 1 && ( + + )} + + {isBtnQueueEnabled && item && playbackManager.canQueue(item) && ( + + )} + + + {isBtnNewCollectionEnabled && } + + + + + + {isBtnFilterEnabled && ( + + )} + + {isBtnSortEnabled && ( + + )} + + {isBtnGridListEnabled && ( + + )} + + + + {isPaginationEnabled && ( - - )} + )} + {isAlphabetPickerEnabled && hasSortName && ( diff --git a/src/apps/experimental/components/library/NewCollectionButton.tsx b/src/apps/experimental/components/library/NewCollectionButton.tsx index 92d9eea31..bfe2c864d 100644 --- a/src/apps/experimental/components/library/NewCollectionButton.tsx +++ b/src/apps/experimental/components/library/NewCollectionButton.tsx @@ -1,6 +1,7 @@ import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; -import AddIcon from '@mui/icons-material/Add'; +import Add from '@mui/icons-material/Add'; +import Button from '@mui/material/Button'; + import globalize from 'lib/globalize'; const NewCollectionButton: FC = () => { @@ -21,13 +22,13 @@ const NewCollectionButton: FC = () => { }, []); return ( - } onClick={showCollectionEditor} > - - + {globalize.translate('NewCollection')} + ); }; diff --git a/src/apps/experimental/components/library/Pagination.tsx b/src/apps/experimental/components/library/Pagination.tsx index 477a98231..782271271 100644 --- a/src/apps/experimental/components/library/Pagination.tsx +++ b/src/apps/experimental/components/library/Pagination.tsx @@ -2,8 +2,9 @@ import React, { FC, useCallback } from 'react'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; import ButtonGroup from '@mui/material/ButtonGroup'; -import IconButton from '@mui/material/IconButton'; +import Stack from '@mui/material/Stack'; import globalize from 'lib/globalize'; import * as userSettings from 'scripts/settings/userSettings'; @@ -47,47 +48,45 @@ const Pagination: FC = ({ }, [limit, setLibraryViewSettings, startIndex]); return ( - - - - {globalize.translate( - 'ListPaging', - recordsStart, - recordsEnd, - totalRecordCount - )} - - {showControls && ( - - - - - - = totalRecordCount || isPlaceholderData } - onClick={onNextPageClick} - > - - - + + {globalize.translate( + 'ListPaging', + recordsStart, + recordsEnd, + totalRecordCount )} - + {showControls && ( + + + + + + )} + ); }; diff --git a/src/apps/experimental/components/library/PlayAllButton.tsx b/src/apps/experimental/components/library/PlayAllButton.tsx index 16cef318a..4e9a1a47d 100644 --- a/src/apps/experimental/components/library/PlayAllButton.tsx +++ b/src/apps/experimental/components/library/PlayAllButton.tsx @@ -1,6 +1,6 @@ import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; -import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import Button from '@mui/material/Button'; +import PlayArrow from '@mui/icons-material/PlayArrow'; import { playbackManager } from 'components/playback/playbackmanager'; import globalize from 'lib/globalize'; @@ -47,19 +47,13 @@ const PlayAllButton: FC = ({ item, items, viewType, hasFilte }, [hasFilters, item, items, libraryViewSettings, viewType]); return ( - } onClick={play} - sx={{ - order: { - xs: 1, - sm: 'unset' - } - }} > - - + {globalize.translate('HeaderPlayAll')} + ); }; diff --git a/src/apps/experimental/components/library/QueueButton.tsx b/src/apps/experimental/components/library/QueueButton.tsx index c656e2b56..aa925e228 100644 --- a/src/apps/experimental/components/library/QueueButton.tsx +++ b/src/apps/experimental/components/library/QueueButton.tsx @@ -1,6 +1,6 @@ import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; -import QueueIcon from '@mui/icons-material/Queue'; +import Button from '@mui/material/Button'; +import Queue from '@mui/icons-material/Queue'; import { playbackManager } from 'components/playback/playbackmanager'; import globalize from 'lib/globalize'; @@ -8,11 +8,17 @@ import type { ItemDto } from 'types/base/models/item-dto'; interface QueueButtonProps { item: ItemDto | undefined - items: ItemDto[]; - hasFilters: boolean; + items: ItemDto[] + hasFilters: boolean + isTextVisible: boolean } -const QueueButton: FC = ({ item, items, hasFilters }) => { +const QueueButton: FC = ({ + item, + items, + hasFilters, + isTextVisible +}) => { const queue = useCallback(() => { if (item && !hasFilters) { playbackManager.queue({ @@ -30,19 +36,17 @@ const QueueButton: FC = ({ item, items, hasFilters }) => { }, [hasFilters, item, items]); return ( - : undefined} onClick={queue} - sx={{ - order: { - xs: 3, - sm: 'unset' - } - }} > - - + {isTextVisible ? ( + globalize.translate('AddToPlayQueue') + ) : ( + + )} + ); }; diff --git a/src/apps/experimental/components/library/ShuffleButton.tsx b/src/apps/experimental/components/library/ShuffleButton.tsx index 44acdbc54..7f240df44 100644 --- a/src/apps/experimental/components/library/ShuffleButton.tsx +++ b/src/apps/experimental/components/library/ShuffleButton.tsx @@ -1,7 +1,7 @@ import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by'; import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; -import ShuffleIcon from '@mui/icons-material/Shuffle'; +import Shuffle from '@mui/icons-material/Shuffle'; +import Button from '@mui/material/Button'; import { playbackManager } from 'components/playback/playbackmanager'; import globalize from 'lib/globalize'; @@ -11,14 +11,22 @@ import { LibraryTab } from 'types/libraryTab'; import type { ItemDto } from 'types/base/models/item-dto'; interface ShuffleButtonProps { - item: ItemDto | undefined; - items: ItemDto[]; + item: ItemDto | undefined + items: ItemDto[] viewType: LibraryTab - hasFilters: boolean; + hasFilters: boolean + isTextVisible: boolean libraryViewSettings: LibraryViewSettings } -const ShuffleButton: FC = ({ item, items, viewType, hasFilters, libraryViewSettings }) => { +const ShuffleButton: FC = ({ + item, + items, + viewType, + hasFilters, + isTextVisible, + libraryViewSettings +}) => { const shuffle = useCallback(() => { if (item && !hasFilters) { playbackManager.shuffle(item); @@ -38,19 +46,17 @@ const ShuffleButton: FC = ({ item, items, viewType, hasFilte }, [hasFilters, item, items, libraryViewSettings, viewType]); return ( - : undefined} onClick={shuffle} - sx={{ - order: { - xs: 2, - sm: 'unset' - } - }} > - - + {isTextVisible ? ( + globalize.translate('Shuffle') + ) : ( + + )} + ); }; diff --git a/src/apps/experimental/components/library/SortButton.tsx b/src/apps/experimental/components/library/SortButton.tsx index 25f290b25..6e8acf8b9 100644 --- a/src/apps/experimental/components/library/SortButton.tsx +++ b/src/apps/experimental/components/library/SortButton.tsx @@ -1,12 +1,11 @@ import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by'; import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order'; import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; import MenuItem from '@mui/material/MenuItem'; import Popover from '@mui/material/Popover'; import Typography from '@mui/material/Typography'; import Divider from '@mui/material/Divider'; -import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; import FormControl from '@mui/material/FormControl'; import Select, { SelectChangeEvent } from '@mui/material/Select'; @@ -149,16 +148,14 @@ const SortButton: FC = ({ const sortMenuOptions = getSortMenuOptions(viewType); return ( - - + = ({ - + ); }; diff --git a/src/apps/experimental/components/library/ViewSettingsButton.tsx b/src/apps/experimental/components/library/ViewSettingsButton.tsx index 2ee46b6f1..b61a41db6 100644 --- a/src/apps/experimental/components/library/ViewSettingsButton.tsx +++ b/src/apps/experimental/components/library/ViewSettingsButton.tsx @@ -1,25 +1,28 @@ import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'; import React, { FC, useCallback } from 'react'; -import IconButton from '@mui/material/IconButton'; -import MenuItem from '@mui/material/MenuItem'; +import Check from '@mui/icons-material/Check'; +import MoreVert from '@mui/icons-material/MoreVert'; +import Button from '@mui/material/Button'; import Checkbox from '@mui/material/Checkbox'; -import Typography from '@mui/material/Typography'; import Divider from '@mui/material/Divider'; -import Box from '@mui/material/Box'; -import InputLabel from '@mui/material/InputLabel'; import FormControl from '@mui/material/FormControl'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormGroup from '@mui/material/FormGroup'; -import Select, { SelectChangeEvent } from '@mui/material/Select'; +import InputLabel from '@mui/material/InputLabel'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import MenuList from '@mui/material/MenuList'; import Popover from '@mui/material/Popover'; -import ViewComfyIcon from '@mui/icons-material/ViewComfy'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import Typography from '@mui/material/Typography'; import globalize from 'lib/globalize'; -import { LibraryViewSettings } from 'types/library'; +import { LibraryViewSettings, ViewMode } from 'types/library'; import { LibraryTab } from 'types/libraryTab'; -const excludedViewType = [ +const IMAGE_TYPE_EXCLUDED_VIEWS = [ LibraryTab.Episodes, LibraryTab.Artists, LibraryTab.AlbumArtists, @@ -71,6 +74,20 @@ const ViewSettingsButton: FC = ({ [setLibraryViewSettings] ); + const onGridViewClick = useCallback(() => { + setLibraryViewSettings(prevState => ({ + ...prevState, + ViewMode: ViewMode.GridView + })); + }, [ setLibraryViewSettings ]); + + const onListViewClick = useCallback(() => { + setLibraryViewSettings(prevState => ({ + ...prevState, + ViewMode: ViewMode.ListView + })); + }, [ setLibraryViewSettings ]); + const onSelectChange = useCallback( (event: SelectChangeEvent) => { setLibraryViewSettings((prevState) => ({ @@ -81,19 +98,18 @@ const ViewSettingsButton: FC = ({ [setLibraryViewSettings] ); - const isVisible = !excludedViewType.includes(viewType); + const isGridView = libraryViewSettings.ViewMode === ViewMode.GridView; + const isImageTypeVisible = !IMAGE_TYPE_EXCLUDED_VIEWS.includes(viewType); return ( - - + = ({ '& .MuiFormControl-root': { m: 1, width: 220 } }} > - {isVisible && ( - - - - {globalize.translate('LabelImageType')} - - - - - )} - - - - - } - label={globalize.translate('ShowTitle')} - /> - {isVisible && ( - - } - label={globalize.translate('ShowYear')} - /> + + + + {isGridView && ( + )} - - } - label={globalize.translate('EnableCardLayout')} - /> - - + + {globalize.translate('GridView')} + + + + {!isGridView && ( + + )} + + {globalize.translate('ListView')} + + + + {isGridView && ( + <> + + {isImageTypeVisible && ( + <> + + + + {globalize.translate('LabelImageType')} + + + + + + + )} + + + + } + label={globalize.translate('ShowTitle')} + /> + {isImageTypeVisible && ( + + } + label={globalize.translate('ShowYear')} + /> + )} + + } + label={globalize.translate('EnableCardLayout')} + /> + + + + )} + - + ); }; diff --git a/src/apps/experimental/components/library/filter/FilterButton.tsx b/src/apps/experimental/components/library/filter/FilterButton.tsx index 9442d8361..aed9d360b 100644 --- a/src/apps/experimental/components/library/filter/FilterButton.tsx +++ b/src/apps/experimental/components/library/filter/FilterButton.tsx @@ -1,8 +1,8 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind'; import React, { FC, useCallback } from 'react'; import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'; -import Box from '@mui/material/Box'; -import FilterListIcon from '@mui/icons-material/FilterList'; +import FilterAlt from '@mui/icons-material/FilterAlt'; +import Button from '@mui/material/Button'; import Popover from '@mui/material/Popover'; import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; import MuiAccordionDetails from '@mui/material/AccordionDetails'; @@ -10,7 +10,6 @@ import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/AccordionSummary'; import Badge from '@mui/material/Badge'; -import IconButton from '@mui/material/IconButton'; import { styled } from '@mui/material/styles'; import Typography from '@mui/material/Typography'; @@ -158,18 +157,16 @@ const FilterButton: FC = ({ }; return ( - - + = ({ )} - + ); }; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 6b6fd9324..a59ef8524 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1666,6 +1666,7 @@ "ViewAlbumArtist": "View album artist", "ViewLyrics": "View lyrics", "ViewPlaybackInfo": "View playback info", + "ViewSettings": "View settings", "Watched": "Watched", "Wednesday": "Wednesday", "WeeklyAt": "{0}s at {1}",