mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 02:13:33 +00:00
disable single attribute per line
This commit is contained in:
parent
92ed8e20c9
commit
8b141d652c
154 changed files with 390 additions and 1800 deletions
|
|
@ -13,15 +13,8 @@ interface ActionRequiredContainerProps {
|
|||
export const ActionRequiredContainer = ({ children, title }: ActionRequiredContainerProps) => (
|
||||
<Stack style={{ cursor: 'default', maxWidth: '700px' }}>
|
||||
<Group>
|
||||
<Icon
|
||||
fill="warn"
|
||||
icon="warn"
|
||||
size="lg"
|
||||
/>
|
||||
<Text
|
||||
size="xl"
|
||||
style={{ textTransform: 'uppercase' }}
|
||||
>
|
||||
<Icon fill="warn" icon="warn" size="lg" />
|
||||
<Text size="xl" style={{ textTransform: 'uppercase' }}>
|
||||
{title}
|
||||
</Text>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -21,18 +21,11 @@ export const ErrorFallback = ({ resetErrorBoundary }: FallbackProps) => {
|
|||
<Center style={{ height: '100vh' }}>
|
||||
<Stack style={{ maxWidth: '50%' }}>
|
||||
<Group gap="xs">
|
||||
<Icon
|
||||
fill="error"
|
||||
icon="error"
|
||||
size="lg"
|
||||
/>
|
||||
<Icon fill="error" icon="error" size="lg" />
|
||||
<Text size="lg">{t('error.genericError')}</Text>
|
||||
</Group>
|
||||
<Text>{error?.message}</Text>
|
||||
<Button
|
||||
onClick={resetErrorBoundary}
|
||||
variant="filled"
|
||||
>
|
||||
<Button onClick={resetErrorBoundary} variant="filled">
|
||||
{t('common.reload')}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
|
|
|||
|
|
@ -43,18 +43,11 @@ export const MpvRequired = () => {
|
|||
<Text>Set your MPV executable location below and restart the application.</Text>
|
||||
<Text>
|
||||
MPV is available at the following:{' '}
|
||||
<a
|
||||
href="https://mpv.io/installation/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://mpv.io/installation/" rel="noreferrer" target="_blank">
|
||||
https://mpv.io/
|
||||
</a>
|
||||
</Text>
|
||||
<FileInput
|
||||
disabled={disabled}
|
||||
onChange={handleSetMpvPath}
|
||||
/>
|
||||
<FileInput disabled={disabled} onChange={handleSetMpvPath} />
|
||||
<Text>{t('setting.disable_mpv', { context: 'description' })}</Text>
|
||||
<Checkbox
|
||||
label={t('setting.disableMpv')}
|
||||
|
|
|
|||
|
|
@ -42,19 +42,12 @@ const RouteErrorBoundary = () => {
|
|||
px={10}
|
||||
variant="subtle"
|
||||
/>
|
||||
<Icon
|
||||
fill="error"
|
||||
icon="error"
|
||||
size="lg"
|
||||
/>
|
||||
<Icon fill="error" icon="error" size="lg" />
|
||||
<Text size="lg">{t('error.genericError')}</Text>
|
||||
</Group>
|
||||
<Divider my={5} />
|
||||
<Text size="sm">{error?.message}</Text>
|
||||
<Group
|
||||
gap="sm"
|
||||
grow
|
||||
>
|
||||
<Group gap="sm" grow>
|
||||
<Button
|
||||
leftSection={<Icon icon="home" />}
|
||||
onClick={handleHome}
|
||||
|
|
@ -81,11 +74,7 @@ const RouteErrorBoundary = () => {
|
|||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group grow>
|
||||
<Button
|
||||
onClick={handleReload}
|
||||
size="md"
|
||||
variant="filled"
|
||||
>
|
||||
<Button onClick={handleReload} size="md" variant="filled">
|
||||
{t('common.reload')}
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -132,10 +132,7 @@ function ServerSelector() {
|
|||
}}
|
||||
variant={server.id === currentServer?.id ? 'filled' : 'default'}
|
||||
>
|
||||
<Group
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Group justify="space-between" w="100%">
|
||||
<Group>
|
||||
<img
|
||||
src={logo}
|
||||
|
|
@ -144,10 +141,7 @@ function ServerSelector() {
|
|||
width: 'var(--theme-font-size-2xl)',
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
fw={600}
|
||||
size="lg"
|
||||
>
|
||||
<Text fw={600} size="lg">
|
||||
{server.name}
|
||||
</Text>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ const ActionRequiredRoute = () => {
|
|||
<AnimatedPage>
|
||||
<PageHeader />
|
||||
<Center style={{ height: '100%', width: '100vw' }}>
|
||||
<Stack
|
||||
gap="xl"
|
||||
style={{ maxWidth: '50%' }}
|
||||
>
|
||||
<Stack gap="xl" style={{ maxWidth: '50%' }}>
|
||||
<Group wrap="nowrap">
|
||||
{displayedCheck && (
|
||||
<ActionRequiredContainer title={displayedCheck.title}>
|
||||
|
|
@ -64,10 +61,7 @@ const ActionRequiredRoute = () => {
|
|||
{canReturnHome && <Navigate to={AppRoute.HOME} />}
|
||||
{/* This should be displayed if a credential is required */}
|
||||
{isCredentialRequired && (
|
||||
<Group
|
||||
justify="center"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group justify="center" wrap="nowrap">
|
||||
<Button
|
||||
fullWidth
|
||||
leftSection={<Icon icon="edit" />}
|
||||
|
|
|
|||
|
|
@ -18,24 +18,14 @@ const InvalidRoute = () => {
|
|||
<AnimatedPage>
|
||||
<Center style={{ height: '100%', width: '100%' }}>
|
||||
<Stack>
|
||||
<Group
|
||||
justify="center"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Icon
|
||||
color="warn"
|
||||
icon="error"
|
||||
/>
|
||||
<Group justify="center" wrap="nowrap">
|
||||
<Icon color="warn" icon="error" />
|
||||
<Text size="xl">
|
||||
{t('error.apiRouteError', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text>{location.pathname}</Text>
|
||||
<ActionIcon
|
||||
icon="arrowLeftS"
|
||||
onClick={() => navigate(-1)}
|
||||
variant="filled"
|
||||
/>
|
||||
<ActionIcon icon="arrowLeftS" onClick={() => navigate(-1)} variant="filled" />
|
||||
</Stack>
|
||||
</Center>
|
||||
</AnimatedPage>
|
||||
|
|
|
|||
|
|
@ -319,17 +319,11 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
|
|||
const mbzId = detailQuery?.data?.mbzId;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.contentContainer}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<div className={styles.contentContainer} ref={cq.ref}>
|
||||
<LibraryBackgroundOverlay backgroundColor={background} />
|
||||
<div className={styles.detailContainer}>
|
||||
<section>
|
||||
<Group
|
||||
gap="sm"
|
||||
justify="space-between"
|
||||
>
|
||||
<Group gap="sm" justify="space-between">
|
||||
<Group>
|
||||
<PlayButton onClick={() => handlePlay(playButtonBehavior)} />
|
||||
<Group gap="xs">
|
||||
|
|
@ -485,11 +479,7 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
|
|||
suppressRowDrag
|
||||
/>
|
||||
</div>
|
||||
<Stack
|
||||
gap="lg"
|
||||
mt="3rem"
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap="lg" mt="3rem" ref={cq.ref}>
|
||||
{cq.height || cq.width ? (
|
||||
<>
|
||||
{carousels
|
||||
|
|
|
|||
|
|
@ -33,15 +33,9 @@ export const AlbumListContent = ({ gridRef, itemCount, tableRef }: AlbumListCont
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
|
||||
<AlbumListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<AlbumListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<AlbumListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<AlbumListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -448,11 +448,7 @@ export const AlbumListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -471,10 +467,7 @@ export const AlbumListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton
|
||||
onToggle={handleToggleSortOrder}
|
||||
sortOrder={filter.sortOrder}
|
||||
/>
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
{server?.type === ServerType.JELLYFIN && (
|
||||
<>
|
||||
<Divider orientation="vertical" />
|
||||
|
|
@ -497,10 +490,7 @@ export const AlbumListHeaderFilters = ({
|
|||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
<FilterButton
|
||||
isActive={!!isFilterApplied}
|
||||
onClick={handleOpenFiltersModal}
|
||||
/>
|
||||
<FilterButton isActive={!!isFilterApplied} onClick={handleOpenFiltersModal} />
|
||||
<RefreshButton onClick={handleRefresh} />
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
|
|
@ -535,10 +525,7 @@ export const AlbumListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
disabledViewTypes={[ListDisplayType.LIST]}
|
||||
|
|
|
|||
|
|
@ -61,15 +61,9 @@ export const AlbumListHeader = ({
|
|||
}, [filter, genreId, refresh, tableRef]);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader backgroundColor="var(--theme-colors-background)">
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.PlayButton
|
||||
onClick={() => handlePlay?.({ playType: playButtonBehavior })}
|
||||
|
|
@ -85,10 +79,7 @@ export const AlbumListHeader = ({
|
|||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
|
|
|
|||
|
|
@ -227,16 +227,9 @@ export const JellyfinAlbumFilters = ({
|
|||
return (
|
||||
<Stack p="0.8rem">
|
||||
{yesNoFilter.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<YesNoSelect
|
||||
onChange={filter.onChange}
|
||||
size="xs"
|
||||
value={filter.value}
|
||||
/>
|
||||
<YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="0.5rem" />
|
||||
|
|
|
|||
|
|
@ -248,28 +248,15 @@ export const NavidromeAlbumFilters = ({
|
|||
return (
|
||||
<Stack p="0.8rem">
|
||||
{yesNoUndefinedFilters.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<YesNoSelect
|
||||
onChange={filter.onChange}
|
||||
size="xs"
|
||||
value={filter.value}
|
||||
/>
|
||||
<YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
|
||||
</Group>
|
||||
))}
|
||||
{toggleFilters.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<Switch
|
||||
checked={filter?.value || false}
|
||||
onChange={filter.onChange}
|
||||
/>
|
||||
<Switch checked={filter?.value || false} onChange={filter.onChange} />
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="0.5rem" />
|
||||
|
|
@ -307,10 +294,7 @@ export const NavidromeAlbumFilters = ({
|
|||
{tagsQuery.data?.enumTags?.length &&
|
||||
tagsQuery.data.enumTags.length > 0 &&
|
||||
tagsQuery.data.enumTags.map((tag) => (
|
||||
<Group
|
||||
grow
|
||||
key={tag.name}
|
||||
>
|
||||
<Group grow key={tag.name}>
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
data={tag.options}
|
||||
|
|
|
|||
|
|
@ -148,15 +148,9 @@ export const SubsonicAlbumFilters = ({
|
|||
return (
|
||||
<Stack p="0.8rem">
|
||||
{toggleFilters.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<Switch
|
||||
checked={filter?.value || false}
|
||||
onChange={filter.onChange}
|
||||
/>
|
||||
<Switch checked={filter?.value || false} onChange={filter.onChange} />
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="0.5rem" />
|
||||
|
|
|
|||
|
|
@ -70,10 +70,7 @@ const AlbumDetailRoute = () => {
|
|||
}}
|
||||
ref={headerRef}
|
||||
/>
|
||||
<AlbumDetailContent
|
||||
background={background}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<AlbumDetailContent background={background} tableRef={tableRef} />
|
||||
</NativeScrollArea>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -144,11 +144,7 @@ const AlbumListRoute = () => {
|
|||
tableRef={tableRef}
|
||||
title={title}
|
||||
/>
|
||||
<AlbumListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<AlbumListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -174,10 +174,7 @@ const DummyAlbumDetailRoute = () => {
|
|||
</Stack>
|
||||
<div className={styles.detailContainer}>
|
||||
<section>
|
||||
<Group
|
||||
gap="sm"
|
||||
justify="space-between"
|
||||
>
|
||||
<Group gap="sm" justify="space-between">
|
||||
<Group>
|
||||
<PlayButton onClick={() => handlePlay()} />
|
||||
<ActionIcon
|
||||
|
|
@ -231,11 +228,7 @@ const DummyAlbumDetailRoute = () => {
|
|||
<section>
|
||||
<Center>
|
||||
<Group mr={5}>
|
||||
<Icon
|
||||
fill="error"
|
||||
icon="error"
|
||||
size={30}
|
||||
/>
|
||||
<Icon fill="error" icon="error" size={30} />
|
||||
</Group>
|
||||
<h2>{t('error.badAlbum', { postProcess: 'sentenceCase' })}</h2>
|
||||
</Center>
|
||||
|
|
|
|||
|
|
@ -202,10 +202,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
order: itemOrder.recentAlbums,
|
||||
title: (
|
||||
<Group align="flex-end">
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
>
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.recentReleases', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
|
|
@ -232,10 +229,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
loading: compilationAlbumsQuery?.isLoading || compilationAlbumsQuery.isFetching,
|
||||
order: itemOrder.compilations,
|
||||
title: (
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
>
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.appearsOn', { postProcess: 'sentenceCase' })}
|
||||
</TextTitle>
|
||||
),
|
||||
|
|
@ -247,10 +241,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
itemType: LibraryItem.ALBUM_ARTIST,
|
||||
order: itemOrder.similarArtists,
|
||||
title: (
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
>
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.relatedArtists', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
|
|
@ -355,19 +346,10 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
detailQuery?.isLoading ||
|
||||
(server?.type === ServerType.NAVIDROME && enabledItem.topSongs && topSongsQuery?.isLoading);
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<div
|
||||
className={styles.contentContainer}
|
||||
ref={cq.ref}
|
||||
/>
|
||||
);
|
||||
if (isLoading) return <div className={styles.contentContainer} ref={cq.ref} />;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.contentContainer}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<div className={styles.contentContainer} ref={cq.ref}>
|
||||
<LibraryBackgroundOverlay backgroundColor={background} />
|
||||
<div className={styles.detailContainer}>
|
||||
<Group gap="md">
|
||||
|
|
@ -481,15 +463,9 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
) : null}
|
||||
<Grid gutter="xl">
|
||||
{biography ? (
|
||||
<Grid.Col
|
||||
order={itemOrder.biography}
|
||||
span={12}
|
||||
>
|
||||
<Grid.Col order={itemOrder.biography} span={12}>
|
||||
<section style={{ maxWidth: '1280px' }}>
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
>
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.about', {
|
||||
artist: detailQuery?.data?.name,
|
||||
})}
|
||||
|
|
@ -499,23 +475,11 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
</Grid.Col>
|
||||
) : null}
|
||||
{showTopSongs ? (
|
||||
<Grid.Col
|
||||
order={itemOrder.topSongs}
|
||||
span={12}
|
||||
>
|
||||
<Grid.Col order={itemOrder.topSongs} span={12}>
|
||||
<section>
|
||||
<Group
|
||||
justify="space-between"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group
|
||||
align="flex-end"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Group align="flex-end" wrap="nowrap">
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.topSongs', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -42,15 +42,9 @@ export const AlbumArtistListContent = ({
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{isGrid ? (
|
||||
<AlbumArtistListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<AlbumArtistListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<AlbumArtistListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<AlbumArtistListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -372,11 +372,7 @@ export const AlbumArtistListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -395,10 +391,7 @@ export const AlbumArtistListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton
|
||||
onToggle={handleToggleSortOrder}
|
||||
sortOrder={filter.sortOrder}
|
||||
/>
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
{server?.type === ServerType.JELLYFIN && (
|
||||
<>
|
||||
<DropdownMenu position="bottom-start">
|
||||
|
|
@ -437,10 +430,7 @@ export const AlbumArtistListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
disabledViewTypes={[ListDisplayType.LIST]}
|
||||
|
|
|
|||
|
|
@ -46,15 +46,9 @@ export const AlbumArtistListHeader = ({
|
|||
}, 500);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('page.albumArtistList.title', { postProcess: 'titleCase' })}
|
||||
|
|
@ -66,18 +60,12 @@ export const AlbumArtistListHeader = ({
|
|||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
<FilterBar>
|
||||
<AlbumArtistListHeaderFilters
|
||||
gridRef={gridRef}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<AlbumArtistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
|
||||
</FilterBar>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -34,15 +34,9 @@ export const ArtistListContent = ({ gridRef, itemCount, tableRef }: ArtistListCo
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{isGrid ? (
|
||||
<ArtistListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<ArtistListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<ArtistListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<ArtistListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -388,11 +388,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -411,19 +407,13 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton
|
||||
onToggle={handleToggleSortOrder}
|
||||
sortOrder={filter.sortOrder}
|
||||
/>
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
{server?.type === ServerType.JELLYFIN && (
|
||||
<>
|
||||
<Divider orientation="vertical" />
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<ActionIcon
|
||||
icon="folder"
|
||||
variant="subtle"
|
||||
/>
|
||||
<ActionIcon icon="folder" variant="subtle" />
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
{musicFoldersQuery.data?.items.map((folder) => (
|
||||
|
|
@ -442,11 +432,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
|
|||
)}
|
||||
{roles.data?.length && (
|
||||
<>
|
||||
<Select
|
||||
data={roles.data}
|
||||
onChange={handleSetRole}
|
||||
value={filter.role}
|
||||
/>
|
||||
<Select data={roles.data} onChange={handleSetRole} value={filter.role} />
|
||||
</>
|
||||
)}
|
||||
<RefreshButton onClick={handleRefresh} />
|
||||
|
|
@ -466,10 +452,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="xs"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
displayType={display}
|
||||
|
|
|
|||
|
|
@ -42,15 +42,9 @@ export const ArtistListHeader = ({ gridRef, itemCount, tableRef }: ArtistListHea
|
|||
}, 500);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('entity.artist_other', { postProcess: 'titleCase' })}
|
||||
|
|
@ -62,18 +56,12 @@ export const ArtistListHeader = ({ gridRef, itemCount, tableRef }: ArtistListHea
|
|||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
<FilterBar>
|
||||
<ArtistListHeaderFilters
|
||||
gridRef={gridRef}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<ArtistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
|
||||
</FilterBar>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,16 +41,8 @@ const ArtistListRoute = () => {
|
|||
return (
|
||||
<AnimatedPage>
|
||||
<ListContext.Provider value={providerValue}>
|
||||
<ArtistListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<ArtistListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<ArtistListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
<ArtistListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -533,10 +533,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
|
||||
openModal({
|
||||
children: (
|
||||
<ConfirmModal
|
||||
loading={removeFromPlaylistMutation.isLoading}
|
||||
onConfirm={confirm}
|
||||
>
|
||||
<ConfirmModal loading={removeFromPlaylistMutation.isLoading} onConfirm={confirm}>
|
||||
{t('common.areYouSure', { postProcess: 'sentenceCase' })}
|
||||
</ConfirmModal>
|
||||
),
|
||||
|
|
@ -922,26 +919,15 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
<Portal>
|
||||
<AnimatePresence>
|
||||
{opened && (
|
||||
<ContextMenu
|
||||
minWidth={125}
|
||||
ref={mergedRef}
|
||||
xPos={ctx.xPos}
|
||||
yPos={ctx.yPos}
|
||||
>
|
||||
<ContextMenu minWidth={125} ref={mergedRef} xPos={ctx.xPos} yPos={ctx.yPos}>
|
||||
<Stack gap={0}>
|
||||
<Stack
|
||||
gap={0}
|
||||
onClick={closeContextMenu}
|
||||
>
|
||||
<Stack gap={0} onClick={closeContextMenu}>
|
||||
{ctx.menuItems?.map((item) => {
|
||||
return (
|
||||
!contextMenuItems[item.id].disabled && (
|
||||
<Fragment key={`context-menu-${item.id}`}>
|
||||
{item.children ? (
|
||||
<HoverCard
|
||||
offset={0}
|
||||
position="right"
|
||||
>
|
||||
<HoverCard offset={0} position="right">
|
||||
<HoverCard.Target>
|
||||
<ContextMenuButton
|
||||
leftIcon={
|
||||
|
|
|
|||
|
|
@ -33,15 +33,9 @@ export const GenreListContent = ({ gridRef, itemCount, tableRef }: AlbumListCont
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
|
||||
<GenreListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<GenreListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<GenreListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<GenreListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -254,11 +254,7 @@ export const GenreListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -277,10 +273,7 @@ export const GenreListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton
|
||||
onToggle={handleToggleSortOrder}
|
||||
sortOrder={filter.sortOrder}
|
||||
/>
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
{server?.type === ServerType.JELLYFIN && (
|
||||
<>
|
||||
<Divider orientation="vertical" />
|
||||
|
|
@ -340,10 +333,7 @@ export const GenreListHeaderFilters = ({
|
|||
</Button>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
disabledViewTypes={[ListDisplayType.LIST]}
|
||||
|
|
|
|||
|
|
@ -40,15 +40,9 @@ export const GenreListHeader = ({ gridRef, itemCount, tableRef }: GenreListHeade
|
|||
}, 500);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('page.genreList.title', { postProcess: 'titleCase' })}
|
||||
|
|
@ -60,10 +54,7 @@ export const GenreListHeader = ({ gridRef, itemCount, tableRef }: GenreListHeade
|
|||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
|
|
|
|||
|
|
@ -42,16 +42,8 @@ const GenreListRoute = () => {
|
|||
return (
|
||||
<AnimatedPage>
|
||||
<ListContext.Provider value={providerValue}>
|
||||
<GenreListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<GenreListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<GenreListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
<GenreListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -81,10 +81,7 @@ const formatArtists = (artists: null | RelatedArtist[] | undefined) =>
|
|||
{artist.name || '—'}
|
||||
</Text>
|
||||
) : (
|
||||
<Text
|
||||
overflow="visible"
|
||||
size="md"
|
||||
>
|
||||
<Text overflow="visible" size="md">
|
||||
{artist.name || '-'}
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -119,17 +116,7 @@ const FormatGenre = (item: Album | AlbumArtist | Playlist | Song) => {
|
|||
};
|
||||
|
||||
const BoolField = (key: boolean) =>
|
||||
key ? (
|
||||
<Icon
|
||||
color="success"
|
||||
icon="check"
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
color="error"
|
||||
icon="x"
|
||||
/>
|
||||
);
|
||||
key ? <Icon color="success" icon="check" /> : <Icon color="error" icon="x" />;
|
||||
|
||||
const AlbumPropertyMapping: ItemDetailRow<Album>[] = [
|
||||
{ key: 'name', label: 'common.title' },
|
||||
|
|
@ -409,12 +396,7 @@ export const ItemDetailsModal = ({ item }: ItemDetailsModalProps) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
highlightOnHover
|
||||
variant="vertical"
|
||||
withRowBorders={false}
|
||||
withTableBorder
|
||||
>
|
||||
<Table highlightOnHover variant="vertical" withRowBorders={false} withTableBorder>
|
||||
<Table.Tbody>{body}</Table.Tbody>
|
||||
</Table>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,10 +22,7 @@ export const SongPath = ({ path }: SongPathProps) => {
|
|||
|
||||
return (
|
||||
<Group>
|
||||
<CopyButton
|
||||
timeout={2000}
|
||||
value={path}
|
||||
>
|
||||
<CopyButton timeout={2000} value={path}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip
|
||||
label={t(
|
||||
|
|
@ -36,10 +33,7 @@ export const SongPath = ({ path }: SongPathProps) => {
|
|||
)}
|
||||
withinPortal
|
||||
>
|
||||
<ActionIcon
|
||||
onClick={copy}
|
||||
variant="transparent"
|
||||
>
|
||||
<ActionIcon onClick={copy} variant="transparent">
|
||||
{copied ? <Icon icon="check" /> : <Icon icon="clipboardCopy" />}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -38,33 +38,15 @@ const SearchResult = ({ data, onClick }: SearchResultProps) => {
|
|||
source === LyricSource.GENIUS ? id.replace(/^((http[s]?|ftp):\/)?\/?([^:/\s]+)/g, '') : id;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={styles.searchItem}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Group
|
||||
justify="space-between"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Stack
|
||||
gap={0}
|
||||
maw="65%"
|
||||
>
|
||||
<Text
|
||||
fw={600}
|
||||
size="md"
|
||||
>
|
||||
<button className={styles.searchItem} onClick={onClick}>
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Stack gap={0} maw="65%">
|
||||
<Text fw={600} size="md">
|
||||
{name}
|
||||
</Text>
|
||||
<Text isMuted>{artist}</Text>
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Text
|
||||
isMuted
|
||||
size="sm"
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<Text isMuted size="sm">
|
||||
{[source, cleanId].join(' — ')}
|
||||
</Text>
|
||||
</Group>
|
||||
|
|
@ -167,11 +149,7 @@ export const LyricsSearchForm = ({ artist, name, onSearchOverride }: LyricSearch
|
|||
export const openLyricSearchModal = ({ artist, name, onSearchOverride }: LyricSearchFormProps) => {
|
||||
openModal({
|
||||
children: (
|
||||
<LyricsSearchForm
|
||||
artist={artist}
|
||||
name={name}
|
||||
onSearchOverride={onSearchOverride}
|
||||
/>
|
||||
<LyricsSearchForm artist={artist} name={name} onSearchOverride={onSearchOverride} />
|
||||
),
|
||||
size: 'lg',
|
||||
title: i18n.t('form.lyricSearch.title', { postProcess: 'titleCase' }) as string,
|
||||
|
|
|
|||
|
|
@ -151,10 +151,7 @@ export const Lyrics = () => {
|
|||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<div className={styles.lyricsContainer}>
|
||||
{isLoadingLyrics ? (
|
||||
<Spinner
|
||||
container
|
||||
size={25}
|
||||
/>
|
||||
<Spinner container size={25} />
|
||||
) : (
|
||||
<AnimatePresence mode="sync">
|
||||
{hasNoLyrics ? (
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@ export const UnsynchronizedLyrics = ({
|
|||
}, [translatedLyrics]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{ gap: `${settings.gapUnsync}px` }}
|
||||
>
|
||||
<div className={styles.container} style={{ gap: `${settings.gapUnsync}px` }}>
|
||||
{settings.showProvider && source && (
|
||||
<LyricLine
|
||||
alignment={settings.alignment}
|
||||
|
|
|
|||
|
|
@ -11,30 +11,17 @@ export const DrawerPlayQueue = () => {
|
|||
const queueRef = useRef<null | { grid: AgGridReactType<Song> }>(null);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
h="100%"
|
||||
>
|
||||
<Flex direction="column" h="100%">
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: 'var(--theme-colors-background)',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
<PlayQueueListControls tableRef={queueRef} type="sideQueue" />
|
||||
</div>
|
||||
<Flex
|
||||
bg="var(--theme-colors-background)"
|
||||
h="100%"
|
||||
mb="0.6rem"
|
||||
>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
<Flex bg="var(--theme-colors-background)" h="100%" mb="0.6rem">
|
||||
<PlayQueue ref={queueRef} type="sideQueue" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -174,10 +174,7 @@ export const PlayQueueListControls = ({ tableRef, type }: PlayQueueListOptionsPr
|
|||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<Popover
|
||||
position="top-end"
|
||||
transitionProps={{ transition: 'fade' }}
|
||||
>
|
||||
<Popover position="top-end" transitionProps={{ transition: 'fade' }}>
|
||||
<Popover.Target>
|
||||
<ActionIcon
|
||||
icon="settings"
|
||||
|
|
|
|||
|
|
@ -18,19 +18,10 @@ export const SidebarPlayQueue = () => {
|
|||
const isWeb = windowBarStyle === Platform.WEB;
|
||||
return (
|
||||
<VirtualGridContainer>
|
||||
<Box
|
||||
display={!isWeb ? 'flex' : undefined}
|
||||
h="65px"
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
<Box display={!isWeb ? 'flex' : undefined} h="65px">
|
||||
<PlayQueueListControls tableRef={queueRef} type="sideQueue" />
|
||||
</Box>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
<PlayQueue ref={queueRef} type="sideQueue" />
|
||||
</VirtualGridContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,14 +16,8 @@ const NowPlayingRoute = () => {
|
|||
<AnimatedPage>
|
||||
<VirtualGridContainer>
|
||||
<NowPlayingHeader />
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
<PlayQueueListControls tableRef={queueRef} type="nowPlaying" />
|
||||
<PlayQueue ref={queueRef} type="nowPlaying" />
|
||||
</VirtualGridContainer>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -115,13 +115,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
<div className={styles.controlsContainer}>
|
||||
<div className={styles.buttonsContainer}>
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaStop"
|
||||
size={buttonSize - 2}
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="default" icon="mediaStop" size={buttonSize - 2} />}
|
||||
onClick={handleStop}
|
||||
tooltip={{
|
||||
label: t('player.stop', { postProcess: 'sentenceCase' }),
|
||||
|
|
@ -152,13 +146,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
variant="tertiary"
|
||||
/>
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaPrevious"
|
||||
size={buttonSize}
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="default" icon="mediaPrevious" size={buttonSize} />}
|
||||
onClick={handlePrevTrack}
|
||||
tooltip={{
|
||||
label: t('player.previous', { postProcess: 'sentenceCase' }),
|
||||
|
|
@ -169,11 +157,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
{skip?.enabled && (
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaStepBackward"
|
||||
size={buttonSize}
|
||||
/>
|
||||
<Icon fill="default" icon="mediaStepBackward" size={buttonSize} />
|
||||
}
|
||||
onClick={() => handleSkipBackward(skip?.skipBackwardSeconds)}
|
||||
tooltip={{
|
||||
|
|
@ -194,13 +178,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
/>
|
||||
{skip?.enabled && (
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaStepForward"
|
||||
size={buttonSize}
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="default" icon="mediaStepForward" size={buttonSize} />}
|
||||
onClick={() => handleSkipForward(skip?.skipForwardSeconds)}
|
||||
tooltip={{
|
||||
label: t('player.skip', {
|
||||
|
|
@ -214,13 +192,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
/>
|
||||
)}
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaNext"
|
||||
size={buttonSize}
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="default" icon="mediaNext" size={buttonSize} />}
|
||||
onClick={handleNextTrack}
|
||||
tooltip={{
|
||||
label: t('player.next', { postProcess: 'sentenceCase' }),
|
||||
|
|
@ -231,11 +203,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
<PlayerButton
|
||||
icon={
|
||||
repeat === PlayerRepeat.ONE ? (
|
||||
<Icon
|
||||
fill="primary"
|
||||
icon="mediaRepeatOne"
|
||||
size={buttonSize}
|
||||
/>
|
||||
<Icon fill="primary" icon="mediaRepeatOne" size={buttonSize} />
|
||||
) : (
|
||||
<Icon
|
||||
fill={repeat === PlayerRepeat.NONE ? 'default' : 'primary'}
|
||||
|
|
@ -268,13 +236,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
variant="tertiary"
|
||||
/>
|
||||
<PlayerButton
|
||||
icon={
|
||||
<Icon
|
||||
fill="default"
|
||||
icon="mediaRandom"
|
||||
size={buttonSize}
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="default" icon="mediaRandom" size={buttonSize} />}
|
||||
onClick={() =>
|
||||
openShuffleAllModal({
|
||||
handlePlayQueueAdd,
|
||||
|
|
@ -291,12 +253,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
</div>
|
||||
<div className={styles.sliderContainer}>
|
||||
<div className={styles.sliderValueWrapper}>
|
||||
<Text
|
||||
fw={600}
|
||||
isMuted
|
||||
isNoSelect
|
||||
size="xs"
|
||||
>
|
||||
<Text fw={600} isMuted isNoSelect size="xs">
|
||||
{formattedTime}
|
||||
</Text>
|
||||
</div>
|
||||
|
|
@ -324,12 +281,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
/>
|
||||
</div>
|
||||
<div className={styles.sliderValueWrapper}>
|
||||
<Text
|
||||
fw={600}
|
||||
isMuted
|
||||
isNoSelect
|
||||
size="xs"
|
||||
>
|
||||
<Text fw={600} isMuted isNoSelect size="xs">
|
||||
{duration}
|
||||
</Text>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -68,11 +68,7 @@ const ImageWithPlaceholder = ({
|
|||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
color="muted"
|
||||
icon="itemAlbum"
|
||||
size="25%"
|
||||
/>
|
||||
<Icon color="muted" icon="itemAlbum" size="25%" />
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
|
@ -167,14 +163,8 @@ export const FullScreenPlayerImage = () => {
|
|||
justify="flex-start"
|
||||
p="1rem"
|
||||
>
|
||||
<div
|
||||
className={styles.imageContainer}
|
||||
ref={mainImageRef}
|
||||
>
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="sync"
|
||||
>
|
||||
<div className={styles.imageContainer} ref={mainImageRef}>
|
||||
<AnimatePresence initial={false} mode="sync">
|
||||
{imageState.current === 0 && (
|
||||
<ImageWithPlaceholder
|
||||
animate="open"
|
||||
|
|
@ -206,18 +196,8 @@ export const FullScreenPlayerImage = () => {
|
|||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<Stack
|
||||
className={styles.metadataContainer}
|
||||
gap="md"
|
||||
maw="100%"
|
||||
>
|
||||
<Text
|
||||
fw={900}
|
||||
lh="1.2"
|
||||
overflow="hidden"
|
||||
size="4xl"
|
||||
w="100%"
|
||||
>
|
||||
<Stack className={styles.metadataContainer} gap="md" maw="100%">
|
||||
<Text fw={900} lh="1.2" overflow="hidden" size="4xl" w="100%">
|
||||
{currentSong?.name}
|
||||
</Text>
|
||||
<Text
|
||||
|
|
@ -257,10 +237,7 @@ export const FullScreenPlayerImage = () => {
|
|||
</Fragment>
|
||||
))}
|
||||
</Text>
|
||||
<Group
|
||||
justify="center"
|
||||
mt="sm"
|
||||
>
|
||||
<Group justify="center" mt="sm">
|
||||
{currentSong?.container && (
|
||||
<Badge variant="transparent">{currentSong?.container}</Badge>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -76,10 +76,7 @@ export const FullScreenPlayerQueue = () => {
|
|||
justify="center"
|
||||
>
|
||||
{headerItems.map((item) => (
|
||||
<div
|
||||
className={styles.headerItemWrapper}
|
||||
key={`tab-${item.label}`}
|
||||
>
|
||||
<div className={styles.headerItemWrapper} key={`tab-${item.label}`}>
|
||||
<Button
|
||||
flex={1}
|
||||
fw="600"
|
||||
|
|
|
|||
|
|
@ -238,10 +238,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
|
|||
})}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Group
|
||||
w="100%"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group w="100%" wrap="nowrap">
|
||||
<Slider
|
||||
defaultValue={lyricConfig.fontSize}
|
||||
label={(e) =>
|
||||
|
|
@ -278,10 +275,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
|
|||
})}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Group
|
||||
w="100%"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group w="100%" wrap="nowrap">
|
||||
<Slider
|
||||
defaultValue={lyricConfig.gap}
|
||||
label={(e) => `Synchronized: ${e}px`}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,5 @@ import { useCurrentSong } from '/@/renderer/store';
|
|||
export const FullScreenSimilarSongs = () => {
|
||||
const currentSong = useCurrentSong();
|
||||
|
||||
return currentSong?.id ? (
|
||||
<SimilarSongsList
|
||||
fullScreen
|
||||
song={currentSong}
|
||||
/>
|
||||
) : null;
|
||||
return currentSong?.id ? <SimilarSongsList fullScreen song={currentSong} /> : null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -69,10 +69,7 @@ export const LeftControls = () => {
|
|||
return (
|
||||
<div className={styles.leftControlsContainer}>
|
||||
<LayoutGroup>
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="popLayout"
|
||||
>
|
||||
<AnimatePresence initial={false} mode="popLayout">
|
||||
{!hideImage && (
|
||||
<div className={styles.imageWrapper}>
|
||||
<motion.div
|
||||
|
|
@ -123,19 +120,9 @@ export const LeftControls = () => {
|
|||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<motion.div
|
||||
className={styles.metadataStack}
|
||||
layout="position"
|
||||
>
|
||||
<div
|
||||
className={styles.lineItem}
|
||||
onClick={stopPropagation}
|
||||
>
|
||||
<Group
|
||||
align="center"
|
||||
gap="xs"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<motion.div className={styles.metadataStack} layout="position">
|
||||
<div className={styles.lineItem} onClick={stopPropagation}>
|
||||
<Group align="center" gap="xs" wrap="nowrap">
|
||||
<Text
|
||||
component={Link}
|
||||
fw={500}
|
||||
|
|
|
|||
|
|
@ -193,13 +193,7 @@ export const RightControls = () => {
|
|||
}, [addToFavoritesMutation, removeFromFavoritesMutation, updateRatingMutation]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align="flex-end"
|
||||
direction="column"
|
||||
h="100%"
|
||||
px="1rem"
|
||||
py="0.5rem"
|
||||
>
|
||||
<Flex align="flex-end" direction="column" h="100%" px="1rem" py="0.5rem">
|
||||
<Group h="calc(100% / 3)">
|
||||
{showRating && (
|
||||
<Rating
|
||||
|
|
@ -209,18 +203,8 @@ export const RightControls = () => {
|
|||
/>
|
||||
)}
|
||||
</Group>
|
||||
<Group
|
||||
align="center"
|
||||
gap="xs"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<DropdownMenu
|
||||
arrowOffset={12}
|
||||
offset={0}
|
||||
position="top-end"
|
||||
width={425}
|
||||
withArrow
|
||||
>
|
||||
<Group align="center" gap="xs" wrap="nowrap">
|
||||
<DropdownMenu arrowOffset={12} offset={0} position="top-end" width={425} withArrow>
|
||||
<DropdownMenu.Target>
|
||||
<ActionIcon
|
||||
icon="mediaSpeed"
|
||||
|
|
|
|||
|
|
@ -33,10 +33,5 @@ export const Visualizer = () => {
|
|||
return () => {};
|
||||
}, [accent, canvasRef, motion, webAudio]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
ref={canvasRef}
|
||||
/>
|
||||
);
|
||||
return <div className={styles.container} ref={canvasRef} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,10 +155,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
|
|||
)}
|
||||
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={onCancel} variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -331,11 +331,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
|||
/>
|
||||
</VirtualGridAutoSizerContainer>
|
||||
{isPaginationEnabled && (
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="wait"
|
||||
presenceAffectsLayout
|
||||
>
|
||||
<AnimatePresence initial={false} mode="wait" presenceAffectsLayout>
|
||||
{page.display === ListDisplayType.TABLE_PAGINATED && (
|
||||
<TablePagination
|
||||
pageKey={playlistId}
|
||||
|
|
|
|||
|
|
@ -469,11 +469,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
|
|
@ -555,10 +551,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||
{server?.type === ServerType.NAVIDROME && !isSmartPlaylist && (
|
||||
<>
|
||||
<DropdownMenu.Divider />
|
||||
<DropdownMenu.Item
|
||||
isDanger
|
||||
onClick={handleToggleShowQueryBuilder}
|
||||
>
|
||||
<DropdownMenu.Item isDanger onClick={handleToggleShowQueryBuilder}>
|
||||
{t('action.toggleSmartPlaylistEditor', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -33,15 +33,9 @@ export const PlaylistListContent = ({ gridRef, itemCount, tableRef }: PlaylistLi
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
|
||||
<PlaylistListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<PlaylistListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<PlaylistListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
<div />
|
||||
</Suspense>
|
||||
|
|
|
|||
|
|
@ -355,11 +355,7 @@ export const PlaylistListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -378,10 +374,7 @@ export const PlaylistListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Divider orientation="vertical" />
|
||||
<OrderToggleButton
|
||||
onToggle={handleToggleSortOrder}
|
||||
sortOrder={filter.sortOrder}
|
||||
/>
|
||||
<OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
|
||||
<RefreshButton onClick={handleRefresh} />
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
|
|
@ -397,14 +390,8 @@ export const PlaylistListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="xs"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Button
|
||||
onClick={handleCreatePlaylistModal}
|
||||
variant="subtle"
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
<Button onClick={handleCreatePlaylistModal} variant="subtle">
|
||||
{t('action.createPlaylist', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
<ListConfigMenu
|
||||
|
|
|
|||
|
|
@ -44,16 +44,9 @@ export const PlaylistListHeader = ({ gridRef, itemCount, tableRef }: PlaylistLis
|
|||
}, 500);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex align="center" justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('page.playlistList.title', { postProcess: 'titleCase' })}
|
||||
|
|
@ -67,18 +60,12 @@ export const PlaylistListHeader = ({ gridRef, itemCount, tableRef }: PlaylistLis
|
|||
</Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
<FilterBar>
|
||||
<PlaylistListHeaderFilters
|
||||
gridRef={gridRef}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
|
||||
</FilterBar>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -410,11 +410,7 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||
];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
h="calc(100% - 2rem)"
|
||||
justify="space-between"
|
||||
>
|
||||
<Flex direction="column" h="calc(100% - 2rem)" justify="space-between">
|
||||
<ScrollArea>
|
||||
<QueryBuilder
|
||||
data={filters}
|
||||
|
|
@ -442,17 +438,8 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||
uniqueId={filters.uniqueId}
|
||||
/>
|
||||
</ScrollArea>
|
||||
<Group
|
||||
align="flex-end"
|
||||
justify="space-between"
|
||||
m="1rem"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group
|
||||
gap="sm"
|
||||
w="100%"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group align="flex-end" justify="space-between" m="1rem" wrap="nowrap">
|
||||
<Group gap="sm" w="100%" wrap="nowrap">
|
||||
<Select
|
||||
data={sortOptions}
|
||||
label="Sort"
|
||||
|
|
@ -485,20 +472,11 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||
/>
|
||||
</Group>
|
||||
{onSave && onSaveAs && (
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Button
|
||||
loading={isSaving}
|
||||
onClick={handleSaveAs}
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<Button loading={isSaving} onClick={handleSaveAs}>
|
||||
{t('common.saveAs', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={openPreviewModal}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={openPreviewModal} variant="subtle">
|
||||
{t('common.preview', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-end">
|
||||
|
|
@ -512,12 +490,7 @@ export const PlaylistQueryBuilder = forwardRef(
|
|||
<DropdownMenu.Dropdown>
|
||||
<DropdownMenu.Item
|
||||
isDanger
|
||||
leftSection={
|
||||
<Icon
|
||||
color="error"
|
||||
icon="save"
|
||||
/>
|
||||
}
|
||||
leftSection={<Icon color="error" icon="save" />}
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t('common.saveAndReplace', { postProcess: 'titleCase' })}
|
||||
|
|
|
|||
|
|
@ -103,10 +103,7 @@ export const SaveAsPlaylistForm = ({
|
|||
/>
|
||||
)}
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={onCancel} variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -140,10 +140,7 @@ export const UpdatePlaylistForm = ({ body, onCancel, query, users }: UpdatePlayl
|
|||
</>
|
||||
)}
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={onCancel} variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -175,12 +175,7 @@ const PlaylistDetailSongListRoute = () => {
|
|||
|
||||
{(isSmartPlaylist || showQueryBuilder) && (
|
||||
<motion.div>
|
||||
<Box
|
||||
h="100%"
|
||||
mah="35vh"
|
||||
p="md"
|
||||
w="100%"
|
||||
>
|
||||
<Box h="100%" mah="35vh" p="md" w="100%">
|
||||
<Group pb="md">
|
||||
<ActionIcon
|
||||
icon={isQueryBuilderExpanded ? 'arrowUpS' : 'arrowDownS'}
|
||||
|
|
|
|||
|
|
@ -50,16 +50,8 @@ const PlaylistListRoute = () => {
|
|||
return (
|
||||
<AnimatedPage>
|
||||
<ListContext.Provider value={providerValue}>
|
||||
<PlaylistListHeader
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListContent
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<PlaylistListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
<PlaylistListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -95,18 +95,11 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
|
|||
header: { display: 'none' },
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
gap="sm"
|
||||
mb="1rem"
|
||||
>
|
||||
<Group gap="sm" mb="1rem">
|
||||
{pages.map((page, index) => (
|
||||
<Fragment key={page}>
|
||||
{index > 0 && ' > '}
|
||||
<Button
|
||||
disabled
|
||||
size="compact-md"
|
||||
variant="default"
|
||||
>
|
||||
<Button disabled size="compact-md" variant="default">
|
||||
{page?.toLocaleUpperCase()}
|
||||
</Button>
|
||||
</Fragment>
|
||||
|
|
@ -267,10 +260,7 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
|
|||
)}
|
||||
</Command.List>
|
||||
</Command>
|
||||
<Box
|
||||
mt="0.5rem"
|
||||
p="0.5rem"
|
||||
>
|
||||
<Box mt="0.5rem" p="0.5rem">
|
||||
<Group justify="space-between">
|
||||
<Command.Loading>
|
||||
{isHome && isLoading && query !== '' && <Spinner />}
|
||||
|
|
|
|||
|
|
@ -56,10 +56,7 @@ export const LibraryCommandItem = ({
|
|||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{ height: '40px', width: '100%' }}
|
||||
>
|
||||
<div
|
||||
className={styles.itemGrid}
|
||||
style={{ '--item-height': '40px' } as CSSProperties}
|
||||
>
|
||||
<div className={styles.itemGrid} style={{ '--item-height': '40px' } as CSSProperties}>
|
||||
<div className={styles.imageWrapper}>
|
||||
<Image
|
||||
alt="cover"
|
||||
|
|
@ -71,21 +68,13 @@ export const LibraryCommandItem = ({
|
|||
</div>
|
||||
<div className={styles.metadataWrapper}>
|
||||
<Text overflow="hidden">{title}</Text>
|
||||
<Text
|
||||
isMuted
|
||||
overflow="hidden"
|
||||
>
|
||||
<Text isMuted overflow="hidden">
|
||||
{subtitle}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
{isHovered && (
|
||||
<Group
|
||||
align="center"
|
||||
gap="sm"
|
||||
justify="flex-end"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group align="center" gap="sm" justify="flex-end" wrap="nowrap">
|
||||
<ActionIcon
|
||||
disabled={disabled}
|
||||
icon="mediaPlay"
|
||||
|
|
|
|||
|
|
@ -49,15 +49,9 @@ export const SearchHeader = ({ navigationId, tableRef }: SearchHeaderProps) => {
|
|||
}, 200);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>Search</LibraryHeaderBar.Title>
|
||||
</LibraryHeaderBar>
|
||||
|
|
|
|||
|
|
@ -16,14 +16,8 @@ const SearchRoute = () => {
|
|||
|
||||
return (
|
||||
<AnimatedPage key={`search-${navigationId}`}>
|
||||
<SearchHeader
|
||||
navigationId={navigationId}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<SearchContent
|
||||
key={`page-${itemType}`}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<SearchHeader navigationId={navigationId} tableRef={tableRef} />
|
||||
<SearchContent key={`page-${itemType}`} tableRef={tableRef} />
|
||||
</AnimatedPage>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,15 +31,8 @@ interface AddServerFormProps {
|
|||
|
||||
function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) {
|
||||
return (
|
||||
<Stack
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<img
|
||||
height="50"
|
||||
src={icon}
|
||||
width="50"
|
||||
/>
|
||||
<Stack align="center" justify="center">
|
||||
<img height="50" src={icon} width="50" />
|
||||
<Text>{label}</Text>
|
||||
</Stack>
|
||||
);
|
||||
|
|
@ -47,30 +40,15 @@ function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) {
|
|||
|
||||
const SERVER_TYPES = [
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={JellyfinIcon}
|
||||
label="Jellyfin"
|
||||
/>
|
||||
),
|
||||
label: <ServerIconWithLabel icon={JellyfinIcon} label="Jellyfin" />,
|
||||
value: ServerType.JELLYFIN,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={NavidromeIcon}
|
||||
label="Navidrome"
|
||||
/>
|
||||
),
|
||||
label: <ServerIconWithLabel icon={NavidromeIcon} label="Navidrome" />,
|
||||
value: ServerType.NAVIDROME,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<ServerIconWithLabel
|
||||
icon={SubsonicIcon}
|
||||
label="OpenSubsonic"
|
||||
/>
|
||||
),
|
||||
label: <ServerIconWithLabel icon={SubsonicIcon} label="OpenSubsonic" />,
|
||||
value: ServerType.SUBSONIC,
|
||||
},
|
||||
];
|
||||
|
|
@ -174,10 +152,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Stack
|
||||
m={5}
|
||||
ref={focusTrapRef}
|
||||
>
|
||||
<Stack m={5} ref={focusTrapRef}>
|
||||
<SegmentedControl
|
||||
data={SERVER_TYPES}
|
||||
disabled={Boolean(serverLock)}
|
||||
|
|
@ -238,15 +213,9 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
|||
{...form.getInputProps('legacyAuth', { type: 'checkbox' })}
|
||||
/>
|
||||
)}
|
||||
<Group
|
||||
grow
|
||||
justify="flex-end"
|
||||
>
|
||||
<Group grow justify="flex-end">
|
||||
{onCancel && (
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={onCancel} variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,7 @@ interface EditServerFormProps {
|
|||
const ModifiedFieldIndicator = () => {
|
||||
return (
|
||||
<Tooltip label={i18n.t('common.modified', { postProcess: 'titleCase' }) as string}>
|
||||
<Icon
|
||||
color="warn"
|
||||
icon="info"
|
||||
/>
|
||||
<Icon color="warn" icon="info" />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
|
@ -193,17 +190,10 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
|||
/>
|
||||
)}
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={onCancel} variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
type="submit"
|
||||
variant="filled"
|
||||
>
|
||||
<Button loading={isLoading} type="submit" variant="filled">
|
||||
{t('common.save', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -66,11 +66,7 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
|||
/>
|
||||
) : (
|
||||
<Stack>
|
||||
<Table
|
||||
layout="fixed"
|
||||
variant="vertical"
|
||||
withTableBorder
|
||||
>
|
||||
<Table layout="fixed" variant="vertical" withTableBorder>
|
||||
<Table.Tbody>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
|
|
|
|||
|
|
@ -73,10 +73,7 @@ export const ServerList = () => {
|
|||
{Object.keys(serverListQuery)?.map((serverId) => {
|
||||
const server = serverListQuery[serverId];
|
||||
return (
|
||||
<Accordion.Item
|
||||
key={server.id}
|
||||
value={server.name}
|
||||
>
|
||||
<Accordion.Item key={server.id} value={server.name}>
|
||||
<Accordion.Control>
|
||||
<Group>
|
||||
<img
|
||||
|
|
@ -103,10 +100,7 @@ export const ServerList = () => {
|
|||
</Accordion.Item>
|
||||
);
|
||||
})}
|
||||
<Group
|
||||
grow
|
||||
pt="md"
|
||||
>
|
||||
<Group grow pt="md">
|
||||
<Button
|
||||
autoFocus
|
||||
leftSection={<Icon icon="add" />}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ export const ContextMenuSettings = () => {
|
|||
<>
|
||||
<SettingsOptions
|
||||
control={
|
||||
<Button
|
||||
onClick={() => setOpen(!open)}
|
||||
size="compact-md"
|
||||
variant="filled"
|
||||
>
|
||||
<Button onClick={() => setOpen(!open)} size="compact-md" variant="filled">
|
||||
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,17 +35,8 @@ export const DraggableItem = ({ handleChangeDisabled, item, value }: DraggableIt
|
|||
const dragControls = useDragControls();
|
||||
|
||||
return (
|
||||
<Reorder.Item
|
||||
as="div"
|
||||
dragControls={dragControls}
|
||||
dragListener={false}
|
||||
value={item}
|
||||
>
|
||||
<Group
|
||||
py="md"
|
||||
style={{ boxShadow: '0 1px 3px rgba(0,0,0,.1)' }}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Reorder.Item as="div" dragControls={dragControls} dragListener={false} value={item}>
|
||||
<Group py="md" style={{ boxShadow: '0 1px 3px rgba(0,0,0,.1)' }} wrap="nowrap">
|
||||
<Checkbox
|
||||
checked={!item.disabled}
|
||||
onChange={(e) => handleChangeDisabled(item.id, e.target.checked)}
|
||||
|
|
|
|||
|
|
@ -73,20 +73,12 @@ export const RemoteSettings = () => {
|
|||
/>
|
||||
),
|
||||
description: (
|
||||
<Text
|
||||
isMuted
|
||||
isNoSelect
|
||||
size="sm"
|
||||
>
|
||||
<Text isMuted isNoSelect size="sm">
|
||||
{t('setting.enableRemote', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
})}{' '}
|
||||
<a
|
||||
href={url}
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<a href={url} rel="noreferrer noopener" target="_blank">
|
||||
{url}
|
||||
</a>
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -240,10 +240,7 @@ export const HotkeyManagerSettings = () => {
|
|||
/>
|
||||
<div className={styles.container}>
|
||||
{filteredBindings.map((binding) => (
|
||||
<Group
|
||||
key={`hotkey-${binding}`}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group key={`hotkey-${binding}`} wrap="nowrap">
|
||||
<TextInput
|
||||
readOnly
|
||||
style={{ userSelect: 'none' }}
|
||||
|
|
|
|||
|
|
@ -244,10 +244,5 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider={!hasFancyAudio}
|
||||
options={audioOptions}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider={!hasFancyAudio} options={audioOptions} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -215,10 +215,5 @@ export const LyricSettings = () => {
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider={false}
|
||||
options={lyricOptions}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider={false} options={lyricOptions} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,11 +218,7 @@ export const MpvSettings = () => {
|
|||
),
|
||||
description: (
|
||||
<Stack gap={0}>
|
||||
<Text
|
||||
isMuted
|
||||
isNoSelect
|
||||
size="sm"
|
||||
>
|
||||
<Text isMuted isNoSelect size="sm">
|
||||
{t('setting.mpvExtraParameters', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
|
|
|
|||
|
|
@ -86,10 +86,5 @@ export const TranscodeSettings = () => {
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider
|
||||
options={transcodeOptions}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider options={transcodeOptions} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,16 +44,9 @@ export const SettingsHeader = ({ setSearch }: SettingsHeaderProps) => {
|
|||
<Flex ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<LibraryHeaderBar>
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex align="center" justify="space-between" w="100%">
|
||||
<Group wrap="nowrap">
|
||||
<Icon
|
||||
icon="settings"
|
||||
size="5xl"
|
||||
/>
|
||||
<Icon icon="settings" size="5xl" />
|
||||
<LibraryHeaderBar.Title>
|
||||
{t('common.setting', { count: 2, postProcess: 'titleCase' })}
|
||||
</LibraryHeaderBar.Title>
|
||||
|
|
@ -65,10 +58,7 @@ export const SettingsHeader = ({ setSearch }: SettingsHeaderProps) => {
|
|||
setSearch(event.target.value.toLocaleLowerCase())
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
onClick={openResetConfirmModal}
|
||||
variant="default"
|
||||
>
|
||||
<Button onClick={openResetConfirmModal} variant="default">
|
||||
{t('common.resetToDefault', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ interface SettingsOptionProps {
|
|||
export const SettingsOptions = ({ control, description, note, title }: SettingsOptionProps) => {
|
||||
return (
|
||||
<>
|
||||
<Group
|
||||
justify="space-between"
|
||||
style={{ alignItems: 'center' }}
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group justify="space-between" style={{ alignItems: 'center' }} wrap="nowrap">
|
||||
<Stack
|
||||
gap="xs"
|
||||
style={{
|
||||
|
|
@ -30,17 +26,11 @@ export const SettingsOptions = ({ control, description, note, title }: SettingsO
|
|||
}}
|
||||
>
|
||||
<Group>
|
||||
<Text
|
||||
isNoSelect
|
||||
size="md"
|
||||
>
|
||||
<Text isNoSelect size="md">
|
||||
{title}
|
||||
</Text>
|
||||
{note && (
|
||||
<Tooltip
|
||||
label={note}
|
||||
openDelay={0}
|
||||
>
|
||||
<Tooltip label={note} openDelay={0}>
|
||||
<Icon icon="info" />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
@ -48,11 +38,7 @@ export const SettingsOptions = ({ control, description, note, title }: SettingsO
|
|||
{React.isValidElement(description) ? (
|
||||
description
|
||||
) : (
|
||||
<Text
|
||||
isMuted
|
||||
isNoSelect
|
||||
size="sm"
|
||||
>
|
||||
<Text isMuted isNoSelect size="sm">
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ export const SettingsSection = ({ divider, options }: SettingsSectionProps) => {
|
|||
return (
|
||||
<>
|
||||
{values.map((option) => (
|
||||
<SettingsOptions
|
||||
key={`option-${option.title}`}
|
||||
{...option}
|
||||
/>
|
||||
<SettingsOptions key={`option-${option.title}`} {...option} />
|
||||
))}
|
||||
{divider !== false && values.length > 0 && <Divider />}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -94,10 +94,5 @@ export const CacheSettings = () => {
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider={false}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider={false} options={options} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,10 +52,5 @@ export const PasswordSettings = () => {
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider={false}
|
||||
options={updateOptions}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider={false} options={updateOptions} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,10 +44,5 @@ export const UpdateSettings = () => {
|
|||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
divider={utils?.isLinux()}
|
||||
options={updateOptions}
|
||||
/>
|
||||
);
|
||||
return <SettingsSection divider={utils?.isLinux()} options={updateOptions} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@ const SettingsRoute = () => {
|
|||
return (
|
||||
<AnimatedPage>
|
||||
<SettingSearchContext.Provider value={search}>
|
||||
<Flex
|
||||
direction="column"
|
||||
h="100%"
|
||||
w="100%"
|
||||
>
|
||||
<Flex direction="column" h="100%" w="100%">
|
||||
<SettingsHeader setSearch={setSearch} />
|
||||
<SettingsContent />
|
||||
</Flex>
|
||||
|
|
|
|||
|
|
@ -14,11 +14,7 @@ interface AnimatedPageProps {
|
|||
export const AnimatedPage = forwardRef(
|
||||
({ children }: AnimatedPageProps, ref: Ref<HTMLDivElement>) => {
|
||||
return (
|
||||
<motion.main
|
||||
className={styles.animatedPage}
|
||||
ref={ref}
|
||||
{...animationProps.fadeIn}
|
||||
>
|
||||
<motion.main className={styles.animatedPage} ref={ref} {...animationProps.fadeIn}>
|
||||
{children}
|
||||
</motion.main>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ import styles from './filter-bar.module.css';
|
|||
|
||||
export const FilterBar = ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={styles.filterBar}
|
||||
{...props}
|
||||
>
|
||||
<div className={styles.filterBar} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,5 @@ interface LibraryBackgroundOverlayProps {
|
|||
}
|
||||
|
||||
export const LibraryBackgroundOverlay = ({ backgroundColor }: LibraryBackgroundOverlayProps) => {
|
||||
return (
|
||||
<div
|
||||
className={styles.root}
|
||||
style={{ backgroundColor }}
|
||||
/>
|
||||
);
|
||||
return <div className={styles.root} style={{ backgroundColor }} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,21 +22,14 @@ interface TitleProps {
|
|||
const HeaderPlayButton = ({ className, ...props }: PlayButtonProps) => {
|
||||
return (
|
||||
<div className={styles.playButtonContainer}>
|
||||
<PlayButton
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
<PlayButton className={className} {...props} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Title = ({ children }: TitleProps) => {
|
||||
return (
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={1}
|
||||
overflow="hidden"
|
||||
>
|
||||
<TextTitle fw={700} order={1} overflow="hidden">
|
||||
{children}
|
||||
</TextTitle>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -84,10 +84,7 @@ export const LibraryHeader = forwardRef(
|
|||
}, [imageUrl, isImageError]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.libraryHeader}
|
||||
ref={ref}
|
||||
>
|
||||
<div className={styles.libraryHeader} ref={ref}>
|
||||
<div
|
||||
className={styles.background}
|
||||
style={{ background, filter: `blur(${blur ?? 0}rem)` }}
|
||||
|
|
@ -130,10 +127,7 @@ export const LibraryHeader = forwardRef(
|
|||
{itemTypeString()}
|
||||
</Text>
|
||||
<h1 className={styles.title}>
|
||||
<AutoTextSize
|
||||
maxFontSizePx={80}
|
||||
mode="box"
|
||||
>
|
||||
<AutoTextSize maxFontSizePx={80} mode="box">
|
||||
{title}
|
||||
</AutoTextSize>
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -16,14 +16,8 @@ import { ListDisplayType } from '/@/shared/types/types';
|
|||
const DISPLAY_TYPES = [
|
||||
{
|
||||
label: (
|
||||
<Stack
|
||||
align="center"
|
||||
p="sm"
|
||||
>
|
||||
<Icon
|
||||
icon="layoutTable"
|
||||
size="lg"
|
||||
/>
|
||||
<Stack align="center" p="sm">
|
||||
<Icon icon="layoutTable" size="lg" />
|
||||
{i18n.t('table.config.view.table', { postProcess: 'sentenceCase' }) as string}
|
||||
</Stack>
|
||||
),
|
||||
|
|
@ -31,14 +25,8 @@ const DISPLAY_TYPES = [
|
|||
},
|
||||
{
|
||||
label: (
|
||||
<Stack
|
||||
align="center"
|
||||
p="sm"
|
||||
>
|
||||
<Icon
|
||||
icon="layoutGrid"
|
||||
size="lg"
|
||||
/>
|
||||
<Stack align="center" p="sm">
|
||||
<Icon icon="layoutGrid" size="lg" />
|
||||
{i18n.t('table.config.view.card', { postProcess: 'sentenceCase' }) as string}
|
||||
</Stack>
|
||||
),
|
||||
|
|
@ -47,14 +35,8 @@ const DISPLAY_TYPES = [
|
|||
{
|
||||
disabled: true,
|
||||
label: (
|
||||
<Stack
|
||||
align="center"
|
||||
p="sm"
|
||||
>
|
||||
<Icon
|
||||
icon="layoutList"
|
||||
size="lg"
|
||||
/>
|
||||
<Stack align="center" p="sm">
|
||||
<Icon icon="layoutList" size="lg" />
|
||||
{i18n.t('table.config.view.list', { postProcess: 'sentenceCase' }) as string}
|
||||
</Stack>
|
||||
),
|
||||
|
|
@ -79,10 +61,7 @@ interface ListConfigMenuProps {
|
|||
|
||||
export const ListConfigMenu = (props: ListConfigMenuProps) => {
|
||||
return (
|
||||
<Popover
|
||||
position="bottom-end"
|
||||
width={300}
|
||||
>
|
||||
<Popover position="bottom-end" width={300}>
|
||||
<Popover.Target>
|
||||
<SettingsButton />
|
||||
</Popover.Target>
|
||||
|
|
@ -161,12 +140,7 @@ const TableConfig = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
variant="vertical"
|
||||
withColumnBorders
|
||||
withRowBorders
|
||||
withTableBorder
|
||||
>
|
||||
<Table variant="vertical" withColumnBorders withRowBorders withTableBorder>
|
||||
<Table.Tbody>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
|
|
@ -199,10 +173,7 @@ const TableConfig = ({
|
|||
</Table.Tr>
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
<ScrollArea
|
||||
allowDragScroll
|
||||
style={{ maxHeight: '200px' }}
|
||||
>
|
||||
<ScrollArea allowDragScroll style={{ maxHeight: '200px' }}>
|
||||
<CheckboxSelect
|
||||
data={tableColumnsData}
|
||||
onChange={onChangeTableColumns}
|
||||
|
|
@ -227,12 +198,7 @@ const GridConfig = ({ itemSize, onChangeItemGap, onChangeItemSize }: GridConfigP
|
|||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
variant="vertical"
|
||||
withColumnBorders
|
||||
withRowBorders
|
||||
withTableBorder
|
||||
>
|
||||
<Table variant="vertical" withColumnBorders withRowBorders withTableBorder>
|
||||
<Table.Tbody>
|
||||
<Table.Tr>
|
||||
<Table.Th w="50%">
|
||||
|
|
|
|||
|
|
@ -46,11 +46,7 @@ export const SearchInput = ({ onChange, ...props }: SearchInputProps) => {
|
|||
{...props}
|
||||
rightSection={
|
||||
ref.current?.value ? (
|
||||
<ActionIcon
|
||||
icon="x"
|
||||
onClick={handleClear}
|
||||
variant="transparent"
|
||||
/>
|
||||
<ActionIcon icon="x" onClick={handleClear} variant="transparent" />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -128,18 +128,10 @@ export const ShareItemContextModal = ({
|
|||
|
||||
<Group justify="flex-end">
|
||||
<Group>
|
||||
<Button
|
||||
onClick={() => closeModal(id)}
|
||||
size="md"
|
||||
variant="subtle"
|
||||
>
|
||||
<Button onClick={() => closeModal(id)} size="md" variant="subtle">
|
||||
{t('common.cancel', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
type="submit"
|
||||
variant="filled"
|
||||
>
|
||||
<Button size="md" type="submit" variant="filled">
|
||||
{t('common.share', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -20,16 +20,8 @@ export const ActionBar = () => {
|
|||
const { open } = useCommandPalette();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Grid
|
||||
display="flex"
|
||||
gutter="sm"
|
||||
px="1rem"
|
||||
w="100%"
|
||||
>
|
||||
<div className={styles.container} ref={cq.ref}>
|
||||
<Grid display="flex" gutter="sm" px="1rem" w="100%">
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
leftSection={<Icon icon="search" />}
|
||||
|
|
@ -44,11 +36,7 @@ export const ActionBar = () => {
|
|||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<Group
|
||||
gap="sm"
|
||||
grow
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="sm" grow wrap="nowrap">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button p="0.5rem">
|
||||
|
|
@ -59,16 +47,10 @@ export const ActionBar = () => {
|
|||
<AppMenu />
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
<Button
|
||||
onClick={() => navigate(-1)}
|
||||
p="0.5rem"
|
||||
>
|
||||
<Button onClick={() => navigate(-1)} p="0.5rem">
|
||||
<Icon icon="arrowLeftS" />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => navigate(1)}
|
||||
p="0.5rem"
|
||||
>
|
||||
<Button onClick={() => navigate(1)} p="0.5rem">
|
||||
<Icon icon="arrowRightS" />
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -9,12 +9,7 @@ interface CollapsedSidebarButtonProps extends ActionIconProps {}
|
|||
export const CollapsedSidebarButton = forwardRef<HTMLButtonElement, CollapsedSidebarButtonProps>(
|
||||
({ children, ...props }: CollapsedSidebarButtonProps, ref) => {
|
||||
return (
|
||||
<ActionIcon
|
||||
className={styles.button}
|
||||
ref={ref}
|
||||
variant="subtle"
|
||||
{...props}
|
||||
>
|
||||
<ActionIcon className={styles.button} ref={ref} variant="subtle" {...props}>
|
||||
{children}
|
||||
</ActionIcon>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -69,21 +69,12 @@ export const CollapsedSidebar = () => {
|
|||
>
|
||||
<ScrollArea>
|
||||
{sidebarCollapsedNavigation && (
|
||||
<Group
|
||||
gap={0}
|
||||
grow
|
||||
>
|
||||
<Group gap={0} grow>
|
||||
<CollapsedSidebarButton onClick={() => navigate(-1)}>
|
||||
<Icon
|
||||
icon="arrowLeftS"
|
||||
size="xl"
|
||||
/>
|
||||
<Icon icon="arrowLeftS" size="xl" />
|
||||
</CollapsedSidebarButton>
|
||||
<CollapsedSidebarButton onClick={() => navigate(1)}>
|
||||
<Icon
|
||||
icon="arrowRightS"
|
||||
size="xl"
|
||||
/>
|
||||
<Icon icon="arrowRightS" size="xl" />
|
||||
</CollapsedSidebarButton>
|
||||
</Group>
|
||||
)}
|
||||
|
|
@ -92,13 +83,7 @@ export const CollapsedSidebar = () => {
|
|||
<CollapsedSidebarItem
|
||||
activeIcon={null}
|
||||
component={Flex}
|
||||
icon={
|
||||
<Icon
|
||||
fill="muted"
|
||||
icon="menu"
|
||||
size="3xl"
|
||||
/>
|
||||
}
|
||||
icon={<Icon fill="muted" icon="menu" size="3xl" />}
|
||||
label={t('common.menu', { postProcess: 'titleCase' })}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
|
|
@ -112,20 +97,9 @@ export const CollapsedSidebar = () => {
|
|||
</DropdownMenu>
|
||||
{sidebarItemsWithRoute.map((item) => (
|
||||
<CollapsedSidebarItem
|
||||
activeIcon={
|
||||
<SidebarIcon
|
||||
active
|
||||
route={item.route}
|
||||
size="25"
|
||||
/>
|
||||
}
|
||||
activeIcon={<SidebarIcon active route={item.route} size="25" />}
|
||||
component={NavLink}
|
||||
icon={
|
||||
<SidebarIcon
|
||||
route={item.route}
|
||||
size="25"
|
||||
/>
|
||||
}
|
||||
icon={<SidebarIcon route={item.route} size="25" />}
|
||||
key={item.id}
|
||||
label={item.label}
|
||||
route={item.route}
|
||||
|
|
|
|||
|
|
@ -53,12 +53,7 @@ const PlaylistRowButton = ({ name, onPlay, to, ...props }: PlaylistRowButtonProp
|
|||
>
|
||||
{name}
|
||||
</SidebarItem>
|
||||
{isHovered && (
|
||||
<RowControls
|
||||
id={to}
|
||||
onPlay={onPlay}
|
||||
/>
|
||||
)}
|
||||
{isHovered && <RowControls id={to} onPlay={onPlay} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -73,11 +68,7 @@ const RowControls = ({
|
|||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Group
|
||||
className={styles.controls}
|
||||
gap="xs"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group className={styles.controls} gap="xs" wrap="nowrap">
|
||||
<ActionIcon
|
||||
icon="mediaPlay"
|
||||
iconProps={{
|
||||
|
|
@ -205,15 +196,8 @@ export const SidebarPlaylistList = () => {
|
|||
|
||||
return (
|
||||
<Accordion.Item value="playlists">
|
||||
<Accordion.Control
|
||||
component="div"
|
||||
role="button"
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<Group
|
||||
justify="space-between"
|
||||
pr="var(--theme-spacing-md)"
|
||||
>
|
||||
<Accordion.Control component="div" role="button" style={{ userSelect: 'none' }}>
|
||||
<Group justify="space-between" pr="var(--theme-spacing-md)">
|
||||
<Text fw={600}>
|
||||
{t('page.sidebar.playlists', {
|
||||
postProcess: 'titleCase',
|
||||
|
|
@ -323,10 +307,7 @@ export const SidebarSharedPlaylistList = () => {
|
|||
return (
|
||||
<Accordion.Item value="shared-playlists">
|
||||
<Accordion.Control>
|
||||
<Text
|
||||
fw={600}
|
||||
variant="secondary"
|
||||
>
|
||||
<Text fw={600} variant="secondary">
|
||||
{t('page.sidebar.shared', {
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -109,10 +109,7 @@ export const Sidebar = () => {
|
|||
})}
|
||||
id="left-sidebar"
|
||||
>
|
||||
<Group
|
||||
grow
|
||||
id="global-search-container"
|
||||
>
|
||||
<Group grow id="global-search-container">
|
||||
<ActionBar />
|
||||
</Group>
|
||||
<ScrollArea
|
||||
|
|
@ -134,10 +131,7 @@ export const Sidebar = () => {
|
|||
>
|
||||
<Accordion.Item value="library">
|
||||
<Accordion.Control>
|
||||
<Text
|
||||
fw={600}
|
||||
variant="secondary"
|
||||
>
|
||||
<Text fw={600} variant="secondary">
|
||||
{t('page.sidebar.myLibrary', {
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
|
|
@ -146,10 +140,7 @@ export const Sidebar = () => {
|
|||
<Accordion.Panel>
|
||||
{sidebarItemsWithRoute.map((item) => {
|
||||
return (
|
||||
<SidebarItem
|
||||
key={`sidebar-${item.route}`}
|
||||
to={item.route}
|
||||
>
|
||||
<SidebarItem key={`sidebar-${item.route}`} to={item.route}>
|
||||
<Group gap="sm">
|
||||
<SidebarIcon
|
||||
active={location.pathname === item.route}
|
||||
|
|
@ -170,10 +161,7 @@ export const Sidebar = () => {
|
|||
)}
|
||||
</Accordion>
|
||||
</ScrollArea>
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="popLayout"
|
||||
>
|
||||
<AnimatePresence initial={false} mode="popLayout">
|
||||
{showImage && (
|
||||
<motion.div
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,7 @@ export const SimilarSongsList = ({ count, fullScreen, song }: SimilarSongsListPr
|
|||
};
|
||||
|
||||
return songQuery.isLoading ? (
|
||||
<Spinner
|
||||
container
|
||||
size={25}
|
||||
/>
|
||||
<Spinner container size={25} />
|
||||
) : (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<VirtualGridAutoSizerContainer>
|
||||
|
|
|
|||
|
|
@ -175,16 +175,9 @@ export const JellyfinSongFilters = ({
|
|||
return (
|
||||
<Stack p="0.8rem">
|
||||
{yesNoFilters.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<YesNoSelect
|
||||
onChange={filter.onChange}
|
||||
size="xs"
|
||||
value={filter.value}
|
||||
/>
|
||||
<YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="0.5rem" />
|
||||
|
|
|
|||
|
|
@ -133,16 +133,9 @@ export const NavidromeSongFilters = ({
|
|||
return (
|
||||
<Stack p="0.8rem">
|
||||
{toggleFilters.map((filter) => (
|
||||
<Group
|
||||
justify="space-between"
|
||||
key={`nd-filter-${filter.label}`}
|
||||
>
|
||||
<Group justify="space-between" key={`nd-filter-${filter.label}`}>
|
||||
<Text>{filter.label}</Text>
|
||||
<YesNoSelect
|
||||
onChange={filter.onChange}
|
||||
size="xs"
|
||||
value={filter.value}
|
||||
/>
|
||||
<YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
|
||||
</Group>
|
||||
))}
|
||||
<Divider my="0.5rem" />
|
||||
|
|
@ -170,10 +163,7 @@ export const NavidromeSongFilters = ({
|
|||
{tagsQuery.data?.enumTags?.length &&
|
||||
tagsQuery.data.enumTags.length > 0 &&
|
||||
tagsQuery.data.enumTags.map((tag) => (
|
||||
<Group
|
||||
grow
|
||||
key={tag.name}
|
||||
>
|
||||
<Group grow key={tag.name}>
|
||||
<SelectWithInvalidData
|
||||
clearable
|
||||
data={tag.options}
|
||||
|
|
|
|||
|
|
@ -35,15 +35,9 @@ export const SongListContent = ({ gridRef, itemCount, tableRef }: SongListConten
|
|||
return (
|
||||
<Suspense fallback={<Spinner container />}>
|
||||
{isGrid ? (
|
||||
<SongListGridView
|
||||
gridRef={gridRef}
|
||||
itemCount={itemCount}
|
||||
/>
|
||||
<SongListGridView gridRef={gridRef} itemCount={itemCount} />
|
||||
) : (
|
||||
<SongListTableView
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
<SongListTableView itemCount={itemCount} tableRef={tableRef} />
|
||||
)}
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -484,11 +484,7 @@ export const SongListHeaderFilters = ({
|
|||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
gap="sm"
|
||||
ref={cq.ref}
|
||||
w="100%"
|
||||
>
|
||||
<Group gap="sm" ref={cq.ref} w="100%">
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button variant="subtle">{sortByLabel}</Button>
|
||||
|
|
@ -534,10 +530,7 @@ export const SongListHeaderFilters = ({
|
|||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
<FilterButton
|
||||
isActive={!!isFilterApplied}
|
||||
onClick={handleOpenFiltersModal}
|
||||
/>
|
||||
<FilterButton isActive={!!isFilterApplied} onClick={handleOpenFiltersModal} />
|
||||
<RefreshButton onClick={handleRefresh} />
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
|
|
@ -578,10 +571,7 @@ export const SongListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
gap="sm"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ListConfigMenu
|
||||
autoFitColumns={table.autoFit}
|
||||
displayType={display}
|
||||
|
|
|
|||
|
|
@ -70,15 +70,9 @@ export const SongListHeader = ({
|
|||
const playButtonBehavior = usePlayButtonBehavior();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<Stack gap={0} ref={cq.ref}>
|
||||
<PageHeader>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
>
|
||||
<Flex justify="space-between" w="100%">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.PlayButton
|
||||
onClick={() => handlePlay?.({ playType: playButtonBehavior })}
|
||||
|
|
@ -93,10 +87,7 @@ export const SongListHeader = ({
|
|||
</LibraryHeaderBar.Badge>
|
||||
</LibraryHeaderBar>
|
||||
<Group>
|
||||
<SearchInput
|
||||
defaultValue={filter.searchTerm}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
|
||||
</Group>
|
||||
</Flex>
|
||||
</PageHeader>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue