mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 18:33:33 +00:00
Redesign sidebar / header and other misc. improvements (#24)
* Remove 1920px max width * Fix position of list controls menu * Match size and color of search input * Adjust library header sizing * Move app menu to sidebar * Increase row buffer on play queue list * Fix query builder styles * Fix playerbar slider track bg * Adjust titlebar styles * Fix invalid modal prop * Various adjustments to detail pages * Fix sidebar height calculation * Fix list null indicators, add filter indicator * Adjust playqueue styles * Fix jellyfin releaseYear normalization * Suppress browser context menu on ag-grid * Add radius to drawer queue -- normalize layout * Add modal styles to provider theme * Fix playlist song list pagination * Add disc number to albums with more than one disc * Fix query builder boolean values * Adjust input placeholder color * Properly handle rating/favorite from context menu on table * Conform dropdown menu styles to context menu * Increase sort type select width * Fix drawer queue radius * Change primary color * Prevent volume wheel from invalid values * Add icons to query builder dropdowns * Update notification styles * Update scrollbar thumb styles * Remove "add to playlist" on smart playlists * Fix "add to playlist" from context menu
This commit is contained in:
parent
d2c0d4c11f
commit
9f2e873366
80 changed files with 1427 additions and 1101 deletions
|
|
@ -3,28 +3,28 @@ import {
|
|||
Button,
|
||||
getColumnDefs,
|
||||
GridCarousel,
|
||||
Text,
|
||||
TextTitle,
|
||||
useFixedTableHeader,
|
||||
VirtualTable,
|
||||
} from '/@/renderer/components';
|
||||
import { ColDef, RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import { ColDef, RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Box, Group, Stack } from '@mantine/core';
|
||||
import { useSetState } from '@mantine/hooks';
|
||||
import { RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
|
||||
import { RiDiscFill, RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
|
||||
import { generatePath, useParams } from 'react-router';
|
||||
import { useAlbumDetail } from '/@/renderer/features/albums/queries/album-detail-query';
|
||||
import { useSongListStore } from '/@/renderer/store';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { PersistedTableColumn, usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import {
|
||||
useHandleGeneralContextMenu,
|
||||
useHandleTableContextMenu,
|
||||
} from '/@/renderer/features/context-menu';
|
||||
import { Play } from '/@/renderer/types';
|
||||
import { Play, ServerType, TableColumn } from '/@/renderer/types';
|
||||
import {
|
||||
ALBUM_CONTEXT_MENU_ITEMS,
|
||||
SONG_CONTEXT_MENU_ITEMS,
|
||||
|
|
@ -34,11 +34,14 @@ import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-que
|
|||
import { AlbumListSort, LibraryItem, QueueSong, SortOrder } from '/@/renderer/api/types';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
|
||||
const isFullWidthRow = (node: RowNode) => {
|
||||
return node.id?.includes('disc-');
|
||||
};
|
||||
|
||||
const ContentContainer = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1920px;
|
||||
padding: 1rem 2rem 5rem;
|
||||
overflow: hidden;
|
||||
|
||||
|
|
@ -61,13 +64,82 @@ export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
|
|||
const cq = useContainerQuery();
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
|
||||
const page = useSongListStore();
|
||||
// TODO: Make this customizable
|
||||
const columnDefs: ColDef[] = useMemo(() => {
|
||||
const userRatingColumn =
|
||||
detailQuery?.data?.serverType !== ServerType.JELLYFIN
|
||||
? [
|
||||
{
|
||||
column: TableColumn.USER_RATING,
|
||||
width: 0,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() =>
|
||||
getColumnDefs(page.table.columns).filter((c) => c.colId !== 'album' && c.colId !== 'artist'),
|
||||
[page.table.columns],
|
||||
);
|
||||
const cols: PersistedTableColumn[] = [
|
||||
{
|
||||
column: TableColumn.TRACK_NUMBER,
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
column: TableColumn.TITLE_COMBINED,
|
||||
width: 0,
|
||||
},
|
||||
|
||||
{
|
||||
column: TableColumn.DURATION,
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
column: TableColumn.BIT_RATE,
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
column: TableColumn.PLAY_COUNT,
|
||||
width: 0,
|
||||
},
|
||||
{
|
||||
column: TableColumn.LAST_PLAYED,
|
||||
width: 0,
|
||||
},
|
||||
...userRatingColumn,
|
||||
{
|
||||
column: TableColumn.USER_FAVORITE,
|
||||
width: 0,
|
||||
},
|
||||
];
|
||||
return getColumnDefs(cols).filter((c) => c.colId !== 'album' && c.colId !== 'artist');
|
||||
}, [detailQuery?.data?.serverType]);
|
||||
|
||||
const getRowHeight = useCallback((params: RowHeightParams) => {
|
||||
if (isFullWidthRow(params.node)) {
|
||||
return 45;
|
||||
}
|
||||
|
||||
return 60;
|
||||
}, []);
|
||||
|
||||
const songsRowData = useMemo(() => {
|
||||
if (!detailQuery.data?.songs) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uniqueDiscNumbers = new Set(detailQuery.data?.songs.map((s) => s.discNumber));
|
||||
|
||||
if (uniqueDiscNumbers.size === 1) {
|
||||
return detailQuery.data?.songs;
|
||||
}
|
||||
|
||||
const rowData: (QueueSong | { id: string; name: string })[] = [];
|
||||
|
||||
for (const discNumber of uniqueDiscNumbers.values()) {
|
||||
const songsByDiscNumber = detailQuery.data?.songs.filter((s) => s.discNumber === discNumber);
|
||||
rowData.push({ id: `disc-${discNumber}`, name: `DISC ${discNumber}` });
|
||||
rowData.push(...songsByDiscNumber);
|
||||
}
|
||||
|
||||
return rowData;
|
||||
}, [detailQuery.data?.songs]);
|
||||
|
||||
const [pagination, setPagination] = useSetState({
|
||||
artist: 0,
|
||||
|
|
@ -261,9 +333,29 @@ export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
|
|||
suppressRowDrag
|
||||
columnDefs={columnDefs}
|
||||
enableCellChangeFlash={false}
|
||||
fullWidthCellRenderer={(data: any) => {
|
||||
if (!data.data) return null;
|
||||
return (
|
||||
<Group
|
||||
align="center"
|
||||
h="100%"
|
||||
spacing="sm"
|
||||
>
|
||||
<RiDiscFill />
|
||||
<Text>{data.data.name}</Text>
|
||||
</Group>
|
||||
);
|
||||
}}
|
||||
getRowHeight={getRowHeight}
|
||||
getRowId={(data) => data.data.id}
|
||||
rowData={detailQuery.data?.songs}
|
||||
rowHeight={60}
|
||||
isFullWidthRow={(data) => {
|
||||
return isFullWidthRow(data.rowNode) || false;
|
||||
}}
|
||||
isRowSelectable={(data) => {
|
||||
if (isFullWidthRow(data.data)) return false;
|
||||
return true;
|
||||
}}
|
||||
rowData={songsRowData}
|
||||
rowSelection="multiple"
|
||||
onCellContextMenu={handleContextMenu}
|
||||
onRowDoubleClicked={handleRowDoubleClick}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export const AlbumListContent = ({
|
|||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || undefined);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
@ -341,7 +341,7 @@ export const AlbumListContent = ({
|
|||
itemGap={20}
|
||||
itemSize={150 + page.grid?.size}
|
||||
itemType={LibraryItem.ALBUM}
|
||||
loading={!itemCount}
|
||||
loading={itemCount === undefined || itemCount === null}
|
||||
minimumBatchSize={40}
|
||||
route={{
|
||||
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
|
||||
|
|
@ -366,7 +366,7 @@ export const AlbumListContent = ({
|
|||
blockLoadDebounceMillis={200}
|
||||
columnDefs={columnDefs}
|
||||
getRowId={(data) => data.data.id}
|
||||
infiniteInitialRowCount={itemCount || 1}
|
||||
infiniteInitialRowCount={itemCount || 100}
|
||||
pagination={isPaginationEnabled}
|
||||
paginationAutoPageSize={isPaginationEnabled}
|
||||
paginationPageSize={page.table.pagination.itemsPerPage || 100}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { MutableRefObject, useCallback, MouseEvent, ChangeEvent } from 'react';
|
||||
import { MutableRefObject, useCallback, MouseEvent, ChangeEvent, useMemo } from 'react';
|
||||
import { IDatasource } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Flex, Group, Stack } from '@mantine/core';
|
||||
|
|
@ -8,13 +8,13 @@ import {
|
|||
RiSortAsc,
|
||||
RiSortDesc,
|
||||
RiFolder2Line,
|
||||
RiFilter3Line,
|
||||
RiMoreFill,
|
||||
RiAddBoxFill,
|
||||
RiPlayFill,
|
||||
RiAddCircleFill,
|
||||
RiRefreshLine,
|
||||
RiSettings3Fill,
|
||||
RiFilterFill,
|
||||
} from 'react-icons/ri';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
|
|
@ -189,7 +189,7 @@ export const AlbumListHeaderFilters = ({
|
|||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || undefined);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
@ -371,6 +371,20 @@ export const AlbumListHeaderFilters = ({
|
|||
}
|
||||
};
|
||||
|
||||
const isFilterApplied = useMemo(() => {
|
||||
const isNavidromeFilterApplied =
|
||||
server?.type === ServerType.NAVIDROME &&
|
||||
page.filter.ndParams &&
|
||||
Object.values(page.filter.ndParams).some((value) => value !== undefined);
|
||||
|
||||
const isJellyfinFilterApplied =
|
||||
server?.type === ServerType.JELLYFIN &&
|
||||
page.filter.jfParams &&
|
||||
Object.values(page.filter.jfParams).some((value) => value !== undefined);
|
||||
|
||||
return isNavidromeFilterApplied || isJellyfinFilterApplied;
|
||||
}, [page.filter.jfParams, page.filter.ndParams, server?.type]);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
|
|
@ -447,15 +461,6 @@ export const AlbumListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
<Button
|
||||
compact
|
||||
fw={600}
|
||||
size="md"
|
||||
variant="subtle"
|
||||
onClick={handleOpenFiltersModal}
|
||||
>
|
||||
{cq.isSm ? 'Filters' : <RiFilter3Line size="1.3rem" />}
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
|
|
@ -495,12 +500,26 @@ export const AlbumListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<Group
|
||||
noWrap
|
||||
spacing="sm"
|
||||
>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
sx={{ svg: { fill: isFilterApplied ? 'var(--primary-color) !important' : undefined } }}
|
||||
tooltip={{ label: 'Filters' }}
|
||||
variant="subtle"
|
||||
onClick={handleOpenFiltersModal}
|
||||
>
|
||||
<RiFilterFill size="1.3rem" />
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-end">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Configure' }}
|
||||
variant="subtle"
|
||||
>
|
||||
<RiSettings3Fill size="1.3rem" />
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ export const AlbumListHeader = ({
|
|||
);
|
||||
|
||||
const albums = api.normalize.albumList(albumsRes, server);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || undefined);
|
||||
params.successCallback(albums?.items || [], albumsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
@ -209,13 +209,11 @@ export const AlbumListHeader = ({
|
|||
<PageHeader backgroundColor="var(--titlebar-bg)">
|
||||
<Flex
|
||||
justify="space-between"
|
||||
py="1rem"
|
||||
w="100%"
|
||||
>
|
||||
<LibraryHeaderBar>
|
||||
<Group noWrap>
|
||||
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
|
||||
<LibraryHeaderBar.Title>{title || 'Albums'}</LibraryHeaderBar.Title>
|
||||
</Group>
|
||||
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
|
||||
<LibraryHeaderBar.Title>{title || 'Albums'}</LibraryHeaderBar.Title>
|
||||
<Paper
|
||||
fw="600"
|
||||
px="1rem"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { MultiSelect, NumberInput, SpinnerIcon, Switch, Text } from '/@/renderer
|
|||
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { AlbumArtistListSort, SortOrder } from '/@/renderer/api/types';
|
||||
import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query';
|
||||
|
||||
|
|
@ -48,30 +47,30 @@ export const JellyfinAlbumFilters = ({
|
|||
},
|
||||
];
|
||||
|
||||
const handleMinYearFilter = debounce((e: number | undefined) => {
|
||||
if (e && (e < 1700 || e > 2300)) return;
|
||||
const handleMinYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
minYear: e,
|
||||
minYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const handleMaxYearFilter = debounce((e: number | undefined) => {
|
||||
if (e && (e < 1700 || e > 2300)) return;
|
||||
const handleMaxYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
maxYear: e,
|
||||
maxYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const handleGenresFilter = debounce((e: string[] | undefined) => {
|
||||
const genreFilterString = e?.join(',');
|
||||
const genreFilterString = e?.length ? e.join(',') : undefined;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
|
|
@ -82,18 +81,16 @@ export const JellyfinAlbumFilters = ({
|
|||
}, 250);
|
||||
|
||||
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||
const [debouncedSearchTerm] = useDebouncedValue(albumArtistSearchTerm, 200);
|
||||
|
||||
const albumArtistListQuery = useAlbumArtistList(
|
||||
{
|
||||
limit: 300,
|
||||
searchTerm: debouncedSearchTerm,
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
},
|
||||
{
|
||||
enabled: debouncedSearchTerm ? debouncedSearchTerm !== '' : false,
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -106,6 +103,17 @@ export const JellyfinAlbumFilters = ({
|
|||
}));
|
||||
}, [albumArtistListQuery?.data?.items]);
|
||||
|
||||
const handleAlbumArtistFilter = (e: string[] | null) => {
|
||||
const albumArtistFilterString = e?.length ? e.join(',') : undefined;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
albumArtistIds: albumArtistFilterString,
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack p="0.8rem">
|
||||
{toggleFilters.map((filter) => (
|
||||
|
|
@ -124,22 +132,22 @@ export const JellyfinAlbumFilters = ({
|
|||
<Divider my="0.5rem" />
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
defaultValue={filter.jfParams?.minYear}
|
||||
hideControls={false}
|
||||
label="From year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
required={!!filter.jfParams?.maxYear}
|
||||
value={filter.jfParams?.minYear}
|
||||
onChange={handleMinYearFilter}
|
||||
onChange={(e) => handleMinYearFilter(e)}
|
||||
/>
|
||||
<NumberInput
|
||||
defaultValue={filter.jfParams?.maxYear}
|
||||
hideControls={false}
|
||||
label="To year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
required={!!filter.jfParams?.minYear}
|
||||
value={filter.jfParams?.maxYear}
|
||||
onChange={handleMaxYearFilter}
|
||||
onChange={(e) => handleMaxYearFilter(e)}
|
||||
/>
|
||||
</Group>
|
||||
<Group grow>
|
||||
|
|
@ -158,12 +166,14 @@ export const JellyfinAlbumFilters = ({
|
|||
clearable
|
||||
searchable
|
||||
data={selectableAlbumArtists}
|
||||
defaultValue={filter.jfParams?.albumArtistIds?.split(',')}
|
||||
disabled={disableArtistFilter}
|
||||
label="Artist"
|
||||
limit={300}
|
||||
placeholder="Type to search for an artist"
|
||||
rightSection={albumArtistListQuery.isFetching ? <SpinnerIcon /> : undefined}
|
||||
searchValue={albumArtistSearchTerm}
|
||||
onChange={handleAlbumArtistFilter}
|
||||
onSearchChange={setAlbumArtistSearchTerm}
|
||||
/>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { ChangeEvent, useMemo, useState } from 'react';
|
|||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import { NumberInput, Switch, Text, Select, SpinnerIcon } from '/@/renderer/components';
|
||||
import { AlbumListFilter, useAlbumListStore, useSetAlbumFilters } from '/@/renderer/store';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query';
|
||||
|
|
@ -86,29 +85,28 @@ export const NavidromeAlbumFilters = ({
|
|||
},
|
||||
];
|
||||
|
||||
const handleYearFilter = debounce((e: number | undefined) => {
|
||||
const handleYearFilter = debounce((e: number | string) => {
|
||||
const updatedFilters = setFilters({
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
year: e,
|
||||
year: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const [albumArtistSearchTerm, setAlbumArtistSearchTerm] = useState<string>('');
|
||||
const [debouncedSearchTerm] = useDebouncedValue(albumArtistSearchTerm, 200);
|
||||
|
||||
const albumArtistListQuery = useAlbumArtistList(
|
||||
{
|
||||
limit: 300,
|
||||
searchTerm: debouncedSearchTerm,
|
||||
// searchTerm: debouncedSearchTerm,
|
||||
sortBy: AlbumArtistListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
},
|
||||
{
|
||||
enabled: debouncedSearchTerm ? debouncedSearchTerm !== '' : false,
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 1,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -121,6 +119,16 @@ export const NavidromeAlbumFilters = ({
|
|||
}));
|
||||
}, [albumArtistListQuery?.data?.items]);
|
||||
|
||||
const handleAlbumArtistFilter = (e: string | null) => {
|
||||
const updatedFilters = setFilters({
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
artist_id: e || undefined,
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack p="0.8rem">
|
||||
{toggleFilters.map((filter) => (
|
||||
|
|
@ -138,12 +146,12 @@ export const NavidromeAlbumFilters = ({
|
|||
<Divider my="0.5rem" />
|
||||
<Group grow>
|
||||
<NumberInput
|
||||
defaultValue={filter.ndParams?.year}
|
||||
hideControls={false}
|
||||
label="Year"
|
||||
max={5000}
|
||||
min={0}
|
||||
value={filter.ndParams?.year}
|
||||
onChange={handleYearFilter}
|
||||
onChange={(e) => handleYearFilter(e)}
|
||||
/>
|
||||
<Select
|
||||
clearable
|
||||
|
|
@ -159,12 +167,14 @@ export const NavidromeAlbumFilters = ({
|
|||
clearable
|
||||
searchable
|
||||
data={selectableAlbumArtists}
|
||||
defaultValue={filter.ndParams?.artist_id}
|
||||
disabled={disableArtistFilter}
|
||||
label="Artist"
|
||||
limit={300}
|
||||
placeholder="Type to search for an artist"
|
||||
rightSection={albumArtistListQuery.isFetching ? <SpinnerIcon /> : undefined}
|
||||
searchValue={albumArtistSearchTerm}
|
||||
onChange={handleAlbumArtistFilter}
|
||||
onSearchChange={setAlbumArtistSearchTerm}
|
||||
/>
|
||||
</Group>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue