disable single attribute per line

This commit is contained in:
jeffvli 2025-07-12 11:17:54 -07:00
parent 92ed8e20c9
commit 8b141d652c
154 changed files with 390 additions and 1800 deletions

View file

@ -8,7 +8,7 @@ arrowParens: always
proseWrap: never proseWrap: never
htmlWhitespaceSensitivity: strict htmlWhitespaceSensitivity: strict
endOfLine: lf endOfLine: lf
singleAttributePerLine: true singleAttributePerLine: false
bracketSpacing: true bracketSpacing: true
plugins: plugins:
- prettier-plugin-packagejson - prettier-plugin-packagejson

View file

@ -22,10 +22,7 @@ export const App = () => {
const { mode, theme } = useAppTheme(isDark ? AppTheme.DEFAULT_DARK : AppTheme.DEFAULT_LIGHT); const { mode, theme } = useAppTheme(isDark ? AppTheme.DEFAULT_DARK : AppTheme.DEFAULT_LIGHT);
return ( return (
<MantineProvider <MantineProvider defaultColorScheme={mode} theme={theme}>
defaultColorScheme={mode}
theme={theme}
>
<Shell /> <Shell />
</MantineProvider> </MantineProvider>
); );

View file

@ -18,17 +18,7 @@ export const ThemeButton = () => {
}} }}
variant="default" variant="default"
> >
{isDark ? ( {isDark ? <Icon icon="themeLight" size={30} /> : <Icon icon="themeDark" size={30} />}
<Icon
icon="themeLight"
size={30}
/>
) : (
<Icon
icon="themeDark"
size={30}
/>
)}
</ActionIcon> </ActionIcon>
); );
}; };

View file

@ -32,17 +32,9 @@ export const RemoteContainer = () => {
const debouncedSetRating = debounce(setRating, 400); const debouncedSetRating = debounce(setRating, 400);
return ( return (
<Stack <Stack gap="md" h="100dvh" w="100%">
gap="md"
h="100dvh"
w="100%"
>
{showImage && ( {showImage && (
<Flex <Flex align="center" justify="center" w="100%">
align="center"
justify="center"
w="100%"
>
<PlayerImage src={song?.imageUrl} /> <PlayerImage src={song?.imageUrl} />
</Flex> </Flex>
)} )}
@ -87,10 +79,7 @@ export const RemoteContainer = () => {
</Group> </Group>
</Stack> </Stack>
)} )}
<Group <Group gap={0} grow>
gap={0}
grow
>
<ActionIcon <ActionIcon
disabled={!id} disabled={!id}
icon="favorite" icon="favorite"
@ -109,10 +98,7 @@ export const RemoteContainer = () => {
/> />
{(song?.serverType === 'navidrome' || song?.serverType === 'subsonic') && ( {(song?.serverType === 'navidrome' || song?.serverType === 'subsonic') && (
<div style={{ margin: 'auto' }}> <div style={{ margin: 'auto' }}>
<Tooltip <Tooltip label="Double click to clear" openDelay={1000}>
label="Double click to clear"
openDelay={1000}
>
<Rating <Rating
onChange={debouncedSetRating} onChange={debouncedSetRating}
onDoubleClick={() => debouncedSetRating(0)} onDoubleClick={() => debouncedSetRating(0)}
@ -123,10 +109,7 @@ export const RemoteContainer = () => {
</div> </div>
)} )}
</Group> </Group>
<Group <Group gap="xs" grow>
gap="xs"
grow
>
<ActionIcon <ActionIcon
disabled={!id} disabled={!id}
icon="mediaPrevious" icon="mediaPrevious"
@ -174,10 +157,7 @@ export const RemoteContainer = () => {
variant="default" variant="default"
/> />
</Group> </Group>
<Group <Group gap="xs" grow>
gap="xs"
grow
>
<ActionIcon <ActionIcon
icon="mediaShuffle" icon="mediaShuffle"
iconProps={{ iconProps={{
@ -232,10 +212,7 @@ export const RemoteContainer = () => {
max={100} max={100}
onChangeEnd={(e) => send({ event: 'volume', volume: e })} onChangeEnd={(e) => send({ event: 'volume', volume: e })}
rightLabel={ rightLabel={
<Text <Text fw={600} size="xs">
fw={600}
size="xs"
>
{volume ?? 0} {volume ?? 0}
</Text> </Text>
} }

View file

@ -13,16 +13,9 @@ export const Shell = () => {
const connected = useConnected(); const connected = useConnected();
return ( return (
<AppShell <AppShell h="100vh" padding="md" w="100vw">
h="100vh"
padding="md"
w="100vw"
>
<AppShell.Header style={{ background: 'var(--theme-colors-surface)' }}> <AppShell.Header style={{ background: 'var(--theme-colors-surface)' }}>
<Grid <Grid px="md" py="sm">
px="md"
py="sm"
>
<Grid.Col span={4}> <Grid.Col span={4}>
<Flex <Flex
align="center" align="center"
@ -33,20 +26,11 @@ export const Shell = () => {
justifySelf: 'flex-start', justifySelf: 'flex-start',
}} }}
> >
<Image <Image fit="contain" height={32} src="/favicon.ico" width={32} />
fit="contain"
height={32}
src="/favicon.ico"
width={32}
/>
</Flex> </Flex>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Group <Group gap="sm" justify="flex-end" wrap="nowrap">
gap="sm"
justify="flex-end"
wrap="nowrap"
>
<ReconnectButton /> <ReconnectButton />
<ImageButton /> <ImageButton />
<ThemeButton /> <ThemeButton />
@ -58,10 +42,7 @@ export const Shell = () => {
{connected ? ( {connected ? (
<RemoteContainer /> <RemoteContainer />
) : ( ) : (
<Center <Center h="100vh" w="100vw">
h="100vh"
w="100vw"
>
<Spinner /> <Spinner />
</Center> </Center>
)} )}

View file

@ -61,10 +61,7 @@ export const WrappedSlider = ({ leftLabel, rightLabel, value, ...props }: Wrappe
const [seek, setSeek] = useState(0); const [seek, setSeek] = useState(0);
return ( return (
<Group <Group align="center" wrap="nowrap">
align="center"
wrap="nowrap"
>
{leftLabel && <Text size="sm">{leftLabel}</Text>} {leftLabel && <Text size="sm">{leftLabel}</Text>}
<PlayerbarSlider <PlayerbarSlider
{...props} {...props}

View file

@ -190,15 +190,8 @@ export const App = () => {
}, [language]); }, [language]);
return ( return (
<MantineProvider <MantineProvider defaultColorScheme={mode as 'dark' | 'light'} theme={theme}>
defaultColorScheme={mode as 'dark' | 'light'} <Notifications containerWidth="300px" position="bottom-center" zIndex={50000} />
theme={theme}
>
<Notifications
containerWidth="300px"
position="bottom-center"
zIndex={50000}
/>
<PlayQueueHandlerContext.Provider value={providerValue}> <PlayQueueHandlerContext.Provider value={providerValue}>
<ContextMenuProvider> <ContextMenuProvider>
<WebAudioContext.Provider value={webAudioProvider}> <WebAudioContext.Provider value={webAudioProvider}>

View file

@ -47,10 +47,7 @@ export const CardControls = ({
return ( return (
<div className={styles.gridCardControlsContainer}> <div className={styles.gridCardControlsContainer}>
<div className={styles.bottomControls}> <div className={styles.bottomControls}>
<button <button className={styles.playButton} onClick={handlePlay}>
className={styles.playButton}
onClick={handlePlay}
>
<Icon icon="mediaPlay" /> <Icon icon="mediaPlay" />
</button> </button>
<Group gap="xs"> <Group gap="xs">

View file

@ -55,14 +55,8 @@ export const PosterCard = ({
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
> >
<Link <Link className={styles.imageContainer} to={path}>
className={styles.imageContainer} <Image className={styles.image} src={data?.imageUrl} />
to={path}
>
<Image
className={styles.image}
src={data?.imageUrl}
/>
<GridCardControls <GridCardControls
handleFavorite={controls.handleFavorite} handleFavorite={controls.handleFavorite}
handlePlayQueueAdd={controls.handlePlayQueueAdd} handlePlayQueueAdd={controls.handlePlayQueueAdd}
@ -72,30 +66,21 @@ export const PosterCard = ({
/> />
</Link> </Link>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<CardRows <CardRows data={data} rows={controls.cardRows} />
data={data}
rows={controls.cardRows}
/>
</div> </div>
</div> </div>
); );
} }
return ( return (
<div <div className={styles.container} key={`placeholder-${uniqueId}-${data.id}`}>
className={styles.container}
key={`placeholder-${uniqueId}-${data.id}`}
>
<div className={styles.imageContainer}> <div className={styles.imageContainer}>
<Skeleton className={styles.image} /> <Skeleton className={styles.image} />
</div> </div>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<Stack gap="xs"> <Stack gap="xs">
{(controls?.cardRows || []).map((row, index) => ( {(controls?.cardRows || []).map((row, index) => (
<Skeleton <Skeleton height={14} key={`${index}-${row.arrayProperty}`} />
height={14}
key={`${index}-${row.arrayProperty}`}
/>
))} ))}
</Stack> </Stack>
</div> </div>

View file

@ -35,14 +35,8 @@ export const ContextMenuButton = forwardRef(
onClick={props.onClick} onClick={props.onClick}
ref={ref} ref={ref}
> >
<Group <Group justify="space-between" w="100%">
justify="space-between" <Group className={styles.left} gap="md">
w="100%"
>
<Group
className={styles.left}
gap="md"
>
{leftIcon} {leftIcon}
{children} {children}
</Group> </Group>

View file

@ -77,11 +77,7 @@ export const FeatureCarousel = ({ data }: FeatureCarouselProps) => {
className={styles.wrapper} className={styles.wrapper}
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: currentItem?.id || '' })} to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: currentItem?.id || '' })}
> >
<AnimatePresence <AnimatePresence custom={direction} initial={false} mode="popLayout">
custom={direction}
initial={false}
mode="popLayout"
>
{data && ( {data && (
<motion.div <motion.div
animate="animate" animate="animate"
@ -101,10 +97,7 @@ export const FeatureCarousel = ({ data }: FeatureCarouselProps) => {
/> />
</div> </div>
<div className={styles.infoColumn}> <div className={styles.infoColumn}>
<Stack <Stack gap="md" style={{ width: '100%' }}>
gap="md"
style={{ width: '100%' }}
>
<div className={styles.titleWrapper}> <div className={styles.titleWrapper}>
<TextTitle <TextTitle
fw={900} fw={900}
@ -117,10 +110,7 @@ export const FeatureCarousel = ({ data }: FeatureCarouselProps) => {
</div> </div>
<div className={styles.titleWrapper}> <div className={styles.titleWrapper}>
{currentItem?.albumArtists.slice(0, 1).map((artist) => ( {currentItem?.albumArtists.slice(0, 1).map((artist) => (
<Text <Text fw={600} key={`carousel-artist-${artist.id}`}>
fw={600}
key={`carousel-artist-${artist.id}`}
>
{artist.name} {artist.name}
</Text> </Text>
))} ))}

View file

@ -60,10 +60,7 @@ const Title = ({ handleNext, handlePrev, label, pagination }: TitleProps) => {
{isValidElement(label) ? ( {isValidElement(label) ? (
label label
) : ( ) : (
<TextTitle <TextTitle order={3} weight={700}>
order={3}
weight={700}
>
{label} {label}
</TextTitle> </TextTitle>
)} )}
@ -280,11 +277,7 @@ export const SwiperGridCarousel = ({
}, []); }, []);
return ( return (
<Stack <Stack className="grid-carousel" gap="md" ref={containerRef as any}>
className="grid-carousel"
gap="md"
ref={containerRef as any}
>
{title ? ( {title ? (
<Title <Title
{...title} {...title}

View file

@ -91,11 +91,7 @@ export const NativeScrollArea = forwardRef(
{...pageHeaderProps} {...pageHeaderProps}
/> />
)} )}
<div <div className={styles.scrollArea} ref={mergedRef} {...props}>
className={styles.scrollArea}
ref={mergedRef}
{...props}
>
{children} {children}
</div> </div>
</> </>

View file

@ -99,10 +99,7 @@ export const QueryBuilder = ({
}; };
return ( return (
<Stack <Stack gap="sm" ml={`${level * 10}px`}>
gap="sm"
ml={`${level * 10}px`}
>
<Group gap="sm"> <Group gap="sm">
<Select <Select
data={FILTER_GROUP_OPTIONS_DATA} data={FILTER_GROUP_OPTIONS_DATA}
@ -112,12 +109,7 @@ export const QueryBuilder = ({
value={data.type} value={data.type}
width="20%" width="20%"
/> />
<ActionIcon <ActionIcon icon="add" onClick={handleAddRule} size="sm" variant="subtle" />
icon="add"
onClick={handleAddRule}
size="sm"
variant="subtle"
/>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<ActionIcon <ActionIcon
@ -150,24 +142,14 @@ export const QueryBuilder = ({
<DropdownMenu.Divider /> <DropdownMenu.Divider />
<DropdownMenu.Item <DropdownMenu.Item
isDanger isDanger
leftSection={ leftSection={<Icon color="error" icon="refresh" />}
<Icon
color="error"
icon="refresh"
/>
}
onClick={onResetFilters} onClick={onResetFilters}
> >
Reset to default Reset to default
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item <DropdownMenu.Item
isDanger isDanger
leftSection={ leftSection={<Icon color="error" icon="delete" />}
<Icon
color="error"
icon="delete"
/>
}
onClick={onClearFilters} onClick={onClearFilters}
> >
Clear filters Clear filters

View file

@ -48,13 +48,7 @@ const QueryValueInput = ({ data, onChange, type, ...props }: any) => {
/> />
); );
case 'date': case 'date':
return ( return <TextInput onChange={onChange} size="sm" {...props} />;
<TextInput
onChange={onChange}
size="sm"
{...props}
/>
);
case 'dateRange': case 'dateRange':
return ( return (
<> <>
@ -92,21 +86,9 @@ const QueryValueInput = ({ data, onChange, type, ...props }: any) => {
/> />
); );
case 'playlist': case 'playlist':
return ( return <Select data={data} onChange={onChange} {...props} />;
<Select
data={data}
onChange={onChange}
{...props}
/>
);
case 'string': case 'string':
return ( return <TextInput onChange={onChange} size="sm" {...props} />;
<TextInput
onChange={onChange}
size="sm"
{...props}
/>
);
default: default:
return <></>; return <></>;
@ -188,10 +170,7 @@ export const QueryBuilderOption = ({
const ml = (level + 1) * 10; const ml = (level + 1) * 10;
return ( return (
<Group <Group gap="sm" ml={ml}>
gap="sm"
ml={ml}
>
<Select <Select
data={filters} data={filters}
maxWidth={170} maxWidth={170}

View file

@ -81,10 +81,7 @@ export const DefaultCard = ({
data?.userFavorite && styles.isFavorite, data?.userFavorite && styles.isFavorite,
)} )}
> >
<Image <Image className={styles.image} src={data?.imageUrl} />
className={styles.image}
src={data?.imageUrl}
/>
<GridCardControls <GridCardControls
handleFavorite={controls.handleFavorite} handleFavorite={controls.handleFavorite}
handlePlayQueueAdd={controls.handlePlayQueueAdd} handlePlayQueueAdd={controls.handlePlayQueueAdd}
@ -95,10 +92,7 @@ export const DefaultCard = ({
/> />
</div> </div>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<CardRows <CardRows data={data} rows={controls.cardRows} />
data={data}
rows={controls.cardRows}
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -86,10 +86,7 @@ export const GridCardControls = ({
onClick={handlePlay} onClick={handlePlay}
variant="filled" variant="filled"
> >
<Icon <Icon icon="mediaPlay" size="xl" />
icon="mediaPlay"
size="xl"
/>
</Button> </Button>
<div className={styles.bottomControls}> <div className={styles.bottomControls}>
{itemType !== LibraryItem.PLAYLIST && ( {itemType !== LibraryItem.PLAYLIST && (

View file

@ -73,17 +73,11 @@ export const PosterCard = ({
margin: controls.itemGap, margin: controls.itemGap,
}} }}
> >
<div <div className={styles.linkContainer} onClick={() => navigate(path)}>
className={styles.linkContainer}
onClick={() => navigate(path)}
>
<div <div
className={`${styles.imageContainer} ${data?.userFavorite ? styles.isFavorite : ''}`} className={`${styles.imageContainer} ${data?.userFavorite ? styles.isFavorite : ''}`}
> >
<Image <Image className={styles.image} src={data?.imageUrl} />
className={styles.image}
src={data?.imageUrl}
/>
<GridCardControls <GridCardControls
handleFavorite={controls.handleFavorite} handleFavorite={controls.handleFavorite}
handlePlayQueueAdd={controls.handlePlayQueueAdd} handlePlayQueueAdd={controls.handlePlayQueueAdd}
@ -95,10 +89,7 @@ export const PosterCard = ({
</div> </div>
</div> </div>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<CardRows <CardRows data={data} rows={controls.cardRows} />
data={data}
rows={controls.cardRows}
/>
</div> </div>
</div> </div>
); );

View file

@ -15,21 +15,14 @@ export const AlbumArtistCell = ({ data, value }: ICellRendererParams) => {
if (value === undefined) { if (value === undefined) {
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Skeleton <Skeleton height="1rem" width="80%" />
height="1rem"
width="80%"
/>
</CellContainer> </CellContainer>
); );
} }
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{value?.map((item: AlbumArtist | Artist, index: number) => ( {value?.map((item: AlbumArtist | Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}> <React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />} {index > 0 && <Separator />}
@ -47,11 +40,7 @@ export const AlbumArtistCell = ({ data, value }: ICellRendererParams) => {
{item.name || '—'} {item.name || '—'}
</Text> </Text>
) : ( ) : (
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{item.name || '—'} {item.name || '—'}
</Text> </Text>
)} )}

View file

@ -15,21 +15,14 @@ export const ArtistCell = ({ data, value }: ICellRendererParams) => {
if (value === undefined) { if (value === undefined) {
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Skeleton <Skeleton height="1rem" width="80%" />
height="1rem"
width="80%"
/>
</CellContainer> </CellContainer>
); );
} }
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{value?.map((item: AlbumArtist | Artist, index: number) => ( {value?.map((item: AlbumArtist | Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}> <React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />} {index > 0 && <Separator />}
@ -47,11 +40,7 @@ export const ArtistCell = ({ data, value }: ICellRendererParams) => {
{item.name || '—'} {item.name || '—'}
</Text> </Text>
) : ( ) : (
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{item.name || '—'} {item.name || '—'}
</Text> </Text>
)} )}

View file

@ -41,11 +41,7 @@ export const CombinedTitleCell = ({
> >
<Skeleton className={styles.image} /> <Skeleton className={styles.image} />
</div> </div>
<Skeleton <Skeleton className={styles.skeletonMetadata} height="1rem" width="80%" />
className={styles.skeletonMetadata}
height="1rem"
width="80%"
/>
</div> </div>
); );
} }
@ -62,11 +58,7 @@ export const CombinedTitleCell = ({
width: `${(node.rowHeight || 40) - 10}px`, width: `${(node.rowHeight || 40) - 10}px`,
}} }}
> >
<Image <Image alt="cover" className={styles.image} src={value.imageUrl} />
alt="cover"
className={styles.image}
src={value.imageUrl}
/>
<ListCoverControls <ListCoverControls
className={styles.playButton} className={styles.playButton}
@ -77,18 +69,10 @@ export const CombinedTitleCell = ({
/> />
</div> </div>
<div className={styles.metadataWrapper}> <div className={styles.metadataWrapper}>
<Text <Text className="current-song-child" overflow="hidden" size="md">
className="current-song-child"
overflow="hidden"
size="md"
>
{value.name} {value.name}
</Text> </Text>
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{artists?.length ? ( {artists?.length ? (
artists.map((artist: AlbumArtist | Artist, index: number) => ( artists.map((artist: AlbumArtist | Artist, index: number) => (
<React.Fragment key={`queue-${rowIndex}-artist-${artist.id}`}> <React.Fragment key={`queue-${rowIndex}-artist-${artist.id}`}>

View file

@ -25,10 +25,7 @@ export const FullWidthDiscCell = ({ api, data, node }: ICellRendererParams) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Group <Group justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<Button <Button
leftSection={isSelected ? <Icon icon="squareCheck" /> : <Icon icon="square" />} leftSection={isSelected ? <Icon icon="squareCheck" /> : <Icon icon="square" />}
onClick={handleToggleDiscNodes} onClick={handleToggleDiscNodes}

View file

@ -23,10 +23,7 @@ export const GenericCell = ({ value, valueFormatted }: ICellRendererParams, opti
if (value === undefined) { if (value === undefined) {
return ( return (
<CellContainer position={position || 'left'}> <CellContainer position={position || 'left'}>
<Skeleton <Skeleton height="1rem" width="80%" />
height="1rem"
width="80%"
/>
</CellContainer> </CellContainer>
); );
} }
@ -45,12 +42,7 @@ export const GenericCell = ({ value, valueFormatted }: ICellRendererParams, opti
{isLink ? displayedValue.value : displayedValue} {isLink ? displayedValue.value : displayedValue}
</Text> </Text>
) : ( ) : (
<Text <Text isMuted={!primary} isNoSelect={false} overflow="hidden" size="md">
isMuted={!primary}
isNoSelect={false}
overflow="hidden"
size="md"
>
{displayedValue} {displayedValue}
</Text> </Text>
)} )}

View file

@ -13,11 +13,7 @@ export const GenreCell = ({ data, value }: ICellRendererParams) => {
const genrePath = useGenreRoute(); const genrePath = useGenreRoute();
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Text <Text isMuted overflow="hidden" size="md">
isMuted
overflow="hidden"
size="md"
>
{value?.map((item: AlbumArtist | Artist, index: number) => ( {value?.map((item: AlbumArtist | Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}> <React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />} {index > 0 && <Separator />}

View file

@ -19,20 +19,14 @@ export const NoteCell = ({ value }: ICellRendererParams) => {
if (value === undefined) { if (value === undefined) {
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Skeleton <Skeleton height="1rem" width="80%" />
height="1rem"
width="80%"
/>
</CellContainer> </CellContainer>
); );
} }
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Text <Text isMuted overflow="hidden">
isMuted
overflow="hidden"
>
{formattedValue} {formattedValue}
</Text> </Text>
</CellContainer> </CellContainer>

View file

@ -26,11 +26,7 @@ export const RatingCell = ({ node, value }: ICellRendererParams) => {
return ( return (
<CellContainer position="center"> <CellContainer position="center">
<Rating <Rating onChange={handleUpdateRating} size="xs" value={value?.userRating} />
onChange={handleUpdateRating}
size="xs"
value={value?.userRating}
/>
</CellContainer> </CellContainer>
); );
}; };

View file

@ -144,15 +144,9 @@ export const RowIndexCell = ({ eGridCell, value }: ICellRendererParams) => {
return ( return (
<CellContainer position="right"> <CellContainer position="right">
{isPlaying && isCurrentSong ? ( {isPlaying && isCurrentSong ? (
<Icon <Icon fill="primary" icon="mediaPlay" />
fill="primary"
icon="mediaPlay"
/>
) : isCurrentSong ? ( ) : isCurrentSong ? (
<Icon <Icon fill="primary" icon="mediaPause" />
fill="primary"
icon="mediaPause"
/>
) : ( ) : (
<Text <Text
className="current-song-child current-song-index" className="current-song-child current-song-index"

View file

@ -8,21 +8,14 @@ export const TitleCell = ({ value }: ICellRendererParams) => {
if (value === undefined) { if (value === undefined) {
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Skeleton <Skeleton height="1rem" width="80%" />
height="1rem"
width="80%"
/>
</CellContainer> </CellContainer>
); );
} }
return ( return (
<CellContainer position="left"> <CellContainer position="left">
<Text <Text className="current-song-child" overflow="hidden" size="md">
className="current-song-child"
overflow="hidden"
size="md"
>
{value} {value}
</Text> </Text>
</CellContainer> </CellContainer>

View file

@ -7,10 +7,5 @@ export interface ICustomHeaderParams extends IHeaderParams {
} }
export const DurationHeader = () => { export const DurationHeader = () => {
return ( return <Icon icon="duration" size="sm" />;
<Icon
icon="duration"
size="sm"
/>
);
}; };

View file

@ -16,36 +16,11 @@ type Options = {
type Presets = 'actions' | 'duration' | 'rowIndex' | 'userFavorite' | 'userRating'; type Presets = 'actions' | 'duration' | 'rowIndex' | 'userFavorite' | 'userRating';
const headerPresets = { const headerPresets = {
actions: ( actions: <Icon icon="ellipsisHorizontal" size="sm" />,
<Icon duration: <Icon icon="duration" size="sm" />,
icon="ellipsisHorizontal" rowIndex: <Icon icon="hash" size="sm" />,
size="sm" userFavorite: <Icon icon="favorite" size="sm" />,
/> userRating: <Icon icon="star" size="sm" />,
),
duration: (
<Icon
icon="duration"
size="sm"
/>
),
rowIndex: (
<Icon
icon="hash"
size="sm"
/>
),
userFavorite: (
<Icon
icon="favorite"
size="sm"
/>
),
userRating: (
<Icon
icon="star"
size="sm"
/>
),
}; };
export const GenericTableHeader = ( export const GenericTableHeader = (

View file

@ -635,15 +635,8 @@ export const VirtualTable = forwardRef(
onNewColumnsLoaded={handleNewColumnsLoaded} onNewColumnsLoaded={handleNewColumnsLoaded}
/> />
{paginationProps && ( {paginationProps && (
<AnimatePresence <AnimatePresence initial={false} mode="wait" presenceAffectsLayout>
initial={false} <TablePagination {...paginationProps} tableRef={tableRef} />
mode="wait"
presenceAffectsLayout
>
<TablePagination
{...paginationProps}
tableRef={tableRef}
/>
</AnimatePresence> </AnimatePresence>
)} )}
</div> </div>

View file

@ -76,10 +76,7 @@ export const TablePagination = ({
ref={containerQuery.ref} ref={containerQuery.ref}
style={{ borderTop: '1px solid var(--theme-generic-border-color)' }} style={{ borderTop: '1px solid var(--theme-generic-border-color)' }}
> >
<Text <Text isMuted size="md">
isMuted
size="md"
>
{containerQuery.isMd ? ( {containerQuery.isMd ? (
<> <>
Showing <b>{currentPageStartIndex}</b> - <b>{currentPageStopIndex}</b> of{' '} Showing <b>{currentPageStartIndex}</b> - <b>{currentPageStopIndex}</b> of{' '}
@ -97,11 +94,7 @@ export const TablePagination = ({
</> </>
)} )}
</Text> </Text>
<Group <Group gap="sm" ref={containerQuery.ref} wrap="nowrap">
gap="sm"
ref={containerQuery.ref}
wrap="nowrap"
>
<Popover <Popover
onClose={() => handlers.close()} onClose={() => handlers.close()}
opened={isGoToPageOpen} opened={isGoToPageOpen}
@ -127,10 +120,7 @@ export const TablePagination = ({
min={1} min={1}
width={70} width={70}
/> />
<Button <Button type="submit" variant="filled">
type="submit"
variant="filled"
>
Go Go
</Button> </Button>
</Group> </Group>

View file

@ -13,15 +13,8 @@ interface ActionRequiredContainerProps {
export const ActionRequiredContainer = ({ children, title }: ActionRequiredContainerProps) => ( export const ActionRequiredContainer = ({ children, title }: ActionRequiredContainerProps) => (
<Stack style={{ cursor: 'default', maxWidth: '700px' }}> <Stack style={{ cursor: 'default', maxWidth: '700px' }}>
<Group> <Group>
<Icon <Icon fill="warn" icon="warn" size="lg" />
fill="warn" <Text size="xl" style={{ textTransform: 'uppercase' }}>
icon="warn"
size="lg"
/>
<Text
size="xl"
style={{ textTransform: 'uppercase' }}
>
{title} {title}
</Text> </Text>
</Group> </Group>

View file

@ -21,18 +21,11 @@ export const ErrorFallback = ({ resetErrorBoundary }: FallbackProps) => {
<Center style={{ height: '100vh' }}> <Center style={{ height: '100vh' }}>
<Stack style={{ maxWidth: '50%' }}> <Stack style={{ maxWidth: '50%' }}>
<Group gap="xs"> <Group gap="xs">
<Icon <Icon fill="error" icon="error" size="lg" />
fill="error"
icon="error"
size="lg"
/>
<Text size="lg">{t('error.genericError')}</Text> <Text size="lg">{t('error.genericError')}</Text>
</Group> </Group>
<Text>{error?.message}</Text> <Text>{error?.message}</Text>
<Button <Button onClick={resetErrorBoundary} variant="filled">
onClick={resetErrorBoundary}
variant="filled"
>
{t('common.reload')} {t('common.reload')}
</Button> </Button>
</Stack> </Stack>

View file

@ -43,18 +43,11 @@ export const MpvRequired = () => {
<Text>Set your MPV executable location below and restart the application.</Text> <Text>Set your MPV executable location below and restart the application.</Text>
<Text> <Text>
MPV is available at the following:{' '} MPV is available at the following:{' '}
<a <a href="https://mpv.io/installation/" rel="noreferrer" target="_blank">
href="https://mpv.io/installation/"
rel="noreferrer"
target="_blank"
>
https://mpv.io/ https://mpv.io/
</a> </a>
</Text> </Text>
<FileInput <FileInput disabled={disabled} onChange={handleSetMpvPath} />
disabled={disabled}
onChange={handleSetMpvPath}
/>
<Text>{t('setting.disable_mpv', { context: 'description' })}</Text> <Text>{t('setting.disable_mpv', { context: 'description' })}</Text>
<Checkbox <Checkbox
label={t('setting.disableMpv')} label={t('setting.disableMpv')}

View file

@ -42,19 +42,12 @@ const RouteErrorBoundary = () => {
px={10} px={10}
variant="subtle" variant="subtle"
/> />
<Icon <Icon fill="error" icon="error" size="lg" />
fill="error"
icon="error"
size="lg"
/>
<Text size="lg">{t('error.genericError')}</Text> <Text size="lg">{t('error.genericError')}</Text>
</Group> </Group>
<Divider my={5} /> <Divider my={5} />
<Text size="sm">{error?.message}</Text> <Text size="sm">{error?.message}</Text>
<Group <Group gap="sm" grow>
gap="sm"
grow
>
<Button <Button
leftSection={<Icon icon="home" />} leftSection={<Icon icon="home" />}
onClick={handleHome} onClick={handleHome}
@ -81,11 +74,7 @@ const RouteErrorBoundary = () => {
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group grow> <Group grow>
<Button <Button onClick={handleReload} size="md" variant="filled">
onClick={handleReload}
size="md"
variant="filled"
>
{t('common.reload')} {t('common.reload')}
</Button> </Button>
</Group> </Group>

View file

@ -132,10 +132,7 @@ function ServerSelector() {
}} }}
variant={server.id === currentServer?.id ? 'filled' : 'default'} variant={server.id === currentServer?.id ? 'filled' : 'default'}
> >
<Group <Group justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<Group> <Group>
<img <img
src={logo} src={logo}
@ -144,10 +141,7 @@ function ServerSelector() {
width: 'var(--theme-font-size-2xl)', width: 'var(--theme-font-size-2xl)',
}} }}
/> />
<Text <Text fw={600} size="lg">
fw={600}
size="lg"
>
{server.name} {server.name}
</Text> </Text>
</Group> </Group>

View file

@ -49,10 +49,7 @@ const ActionRequiredRoute = () => {
<AnimatedPage> <AnimatedPage>
<PageHeader /> <PageHeader />
<Center style={{ height: '100%', width: '100vw' }}> <Center style={{ height: '100%', width: '100vw' }}>
<Stack <Stack gap="xl" style={{ maxWidth: '50%' }}>
gap="xl"
style={{ maxWidth: '50%' }}
>
<Group wrap="nowrap"> <Group wrap="nowrap">
{displayedCheck && ( {displayedCheck && (
<ActionRequiredContainer title={displayedCheck.title}> <ActionRequiredContainer title={displayedCheck.title}>
@ -64,10 +61,7 @@ const ActionRequiredRoute = () => {
{canReturnHome && <Navigate to={AppRoute.HOME} />} {canReturnHome && <Navigate to={AppRoute.HOME} />}
{/* This should be displayed if a credential is required */} {/* This should be displayed if a credential is required */}
{isCredentialRequired && ( {isCredentialRequired && (
<Group <Group justify="center" wrap="nowrap">
justify="center"
wrap="nowrap"
>
<Button <Button
fullWidth fullWidth
leftSection={<Icon icon="edit" />} leftSection={<Icon icon="edit" />}

View file

@ -18,24 +18,14 @@ const InvalidRoute = () => {
<AnimatedPage> <AnimatedPage>
<Center style={{ height: '100%', width: '100%' }}> <Center style={{ height: '100%', width: '100%' }}>
<Stack> <Stack>
<Group <Group justify="center" wrap="nowrap">
justify="center" <Icon color="warn" icon="error" />
wrap="nowrap"
>
<Icon
color="warn"
icon="error"
/>
<Text size="xl"> <Text size="xl">
{t('error.apiRouteError', { postProcess: 'sentenceCase' })} {t('error.apiRouteError', { postProcess: 'sentenceCase' })}
</Text> </Text>
</Group> </Group>
<Text>{location.pathname}</Text> <Text>{location.pathname}</Text>
<ActionIcon <ActionIcon icon="arrowLeftS" onClick={() => navigate(-1)} variant="filled" />
icon="arrowLeftS"
onClick={() => navigate(-1)}
variant="filled"
/>
</Stack> </Stack>
</Center> </Center>
</AnimatedPage> </AnimatedPage>

View file

@ -319,17 +319,11 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
const mbzId = detailQuery?.data?.mbzId; const mbzId = detailQuery?.data?.mbzId;
return ( return (
<div <div className={styles.contentContainer} ref={cq.ref}>
className={styles.contentContainer}
ref={cq.ref}
>
<LibraryBackgroundOverlay backgroundColor={background} /> <LibraryBackgroundOverlay backgroundColor={background} />
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<section> <section>
<Group <Group gap="sm" justify="space-between">
gap="sm"
justify="space-between"
>
<Group> <Group>
<PlayButton onClick={() => handlePlay(playButtonBehavior)} /> <PlayButton onClick={() => handlePlay(playButtonBehavior)} />
<Group gap="xs"> <Group gap="xs">
@ -485,11 +479,7 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
suppressRowDrag suppressRowDrag
/> />
</div> </div>
<Stack <Stack gap="lg" mt="3rem" ref={cq.ref}>
gap="lg"
mt="3rem"
ref={cq.ref}
>
{cq.height || cq.width ? ( {cq.height || cq.width ? (
<> <>
{carousels {carousels

View file

@ -33,15 +33,9 @@ export const AlbumListContent = ({ gridRef, itemCount, tableRef }: AlbumListCont
return ( return (
<Suspense fallback={<Spinner container />}> <Suspense fallback={<Spinner container />}>
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? ( {display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
<AlbumListGridView <AlbumListGridView gridRef={gridRef} itemCount={itemCount} />
gridRef={gridRef}
itemCount={itemCount}
/>
) : ( ) : (
<AlbumListTableView <AlbumListTableView itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
)} )}
</Suspense> </Suspense>
); );

View file

@ -448,11 +448,7 @@ export const AlbumListHeaderFilters = ({
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button variant="subtle">{sortByLabel}</Button> <Button variant="subtle">{sortByLabel}</Button>
@ -471,10 +467,7 @@ export const AlbumListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<OrderToggleButton <OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
onToggle={handleToggleSortOrder}
sortOrder={filter.sortOrder}
/>
{server?.type === ServerType.JELLYFIN && ( {server?.type === ServerType.JELLYFIN && (
<> <>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
@ -497,10 +490,7 @@ export const AlbumListHeaderFilters = ({
</DropdownMenu> </DropdownMenu>
</> </>
)} )}
<FilterButton <FilterButton isActive={!!isFilterApplied} onClick={handleOpenFiltersModal} />
isActive={!!isFilterApplied}
onClick={handleOpenFiltersModal}
/>
<RefreshButton onClick={handleRefresh} /> <RefreshButton onClick={handleRefresh} />
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
@ -535,10 +525,7 @@ export const AlbumListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group <Group gap="sm" wrap="nowrap">
gap="sm"
wrap="nowrap"
>
<ListConfigMenu <ListConfigMenu
autoFitColumns={table.autoFit} autoFitColumns={table.autoFit}
disabledViewTypes={[ListDisplayType.LIST]} disabledViewTypes={[ListDisplayType.LIST]}

View file

@ -61,15 +61,9 @@ export const AlbumListHeader = ({
}, [filter, genreId, refresh, tableRef]); }, [filter, genreId, refresh, tableRef]);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader backgroundColor="var(--theme-colors-background)"> <PageHeader backgroundColor="var(--theme-colors-background)">
<Flex <Flex justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.PlayButton <LibraryHeaderBar.PlayButton
onClick={() => handlePlay?.({ playType: playButtonBehavior })} onClick={() => handlePlay?.({ playType: playButtonBehavior })}
@ -85,10 +79,7 @@ export const AlbumListHeader = ({
</LibraryHeaderBar.Badge> </LibraryHeaderBar.Badge>
</LibraryHeaderBar> </LibraryHeaderBar>
<Group> <Group>
<SearchInput <SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
defaultValue={filter.searchTerm}
onChange={handleSearch}
/>
</Group> </Group>
</Flex> </Flex>
</PageHeader> </PageHeader>

View file

@ -227,16 +227,9 @@ export const JellyfinAlbumFilters = ({
return ( return (
<Stack p="0.8rem"> <Stack p="0.8rem">
{yesNoFilter.map((filter) => ( {yesNoFilter.map((filter) => (
<Group <Group justify="space-between" key={`nd-filter-${filter.label}`}>
justify="space-between"
key={`nd-filter-${filter.label}`}
>
<Text>{filter.label}</Text> <Text>{filter.label}</Text>
<YesNoSelect <YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
onChange={filter.onChange}
size="xs"
value={filter.value}
/>
</Group> </Group>
))} ))}
<Divider my="0.5rem" /> <Divider my="0.5rem" />

View file

@ -248,28 +248,15 @@ export const NavidromeAlbumFilters = ({
return ( return (
<Stack p="0.8rem"> <Stack p="0.8rem">
{yesNoUndefinedFilters.map((filter) => ( {yesNoUndefinedFilters.map((filter) => (
<Group <Group justify="space-between" key={`nd-filter-${filter.label}`}>
justify="space-between"
key={`nd-filter-${filter.label}`}
>
<Text>{filter.label}</Text> <Text>{filter.label}</Text>
<YesNoSelect <YesNoSelect onChange={filter.onChange} size="xs" value={filter.value} />
onChange={filter.onChange}
size="xs"
value={filter.value}
/>
</Group> </Group>
))} ))}
{toggleFilters.map((filter) => ( {toggleFilters.map((filter) => (
<Group <Group justify="space-between" key={`nd-filter-${filter.label}`}>
justify="space-between"
key={`nd-filter-${filter.label}`}
>
<Text>{filter.label}</Text> <Text>{filter.label}</Text>
<Switch <Switch checked={filter?.value || false} onChange={filter.onChange} />
checked={filter?.value || false}
onChange={filter.onChange}
/>
</Group> </Group>
))} ))}
<Divider my="0.5rem" /> <Divider my="0.5rem" />
@ -307,10 +294,7 @@ export const NavidromeAlbumFilters = ({
{tagsQuery.data?.enumTags?.length && {tagsQuery.data?.enumTags?.length &&
tagsQuery.data.enumTags.length > 0 && tagsQuery.data.enumTags.length > 0 &&
tagsQuery.data.enumTags.map((tag) => ( tagsQuery.data.enumTags.map((tag) => (
<Group <Group grow key={tag.name}>
grow
key={tag.name}
>
<SelectWithInvalidData <SelectWithInvalidData
clearable clearable
data={tag.options} data={tag.options}

View file

@ -148,15 +148,9 @@ export const SubsonicAlbumFilters = ({
return ( return (
<Stack p="0.8rem"> <Stack p="0.8rem">
{toggleFilters.map((filter) => ( {toggleFilters.map((filter) => (
<Group <Group justify="space-between" key={`nd-filter-${filter.label}`}>
justify="space-between"
key={`nd-filter-${filter.label}`}
>
<Text>{filter.label}</Text> <Text>{filter.label}</Text>
<Switch <Switch checked={filter?.value || false} onChange={filter.onChange} />
checked={filter?.value || false}
onChange={filter.onChange}
/>
</Group> </Group>
))} ))}
<Divider my="0.5rem" /> <Divider my="0.5rem" />

View file

@ -70,10 +70,7 @@ const AlbumDetailRoute = () => {
}} }}
ref={headerRef} ref={headerRef}
/> />
<AlbumDetailContent <AlbumDetailContent background={background} tableRef={tableRef} />
background={background}
tableRef={tableRef}
/>
</NativeScrollArea> </NativeScrollArea>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -144,11 +144,7 @@ const AlbumListRoute = () => {
tableRef={tableRef} tableRef={tableRef}
title={title} title={title}
/> />
<AlbumListContent <AlbumListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
gridRef={gridRef}
itemCount={itemCount}
tableRef={tableRef}
/>
</ListContext.Provider> </ListContext.Provider>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -174,10 +174,7 @@ const DummyAlbumDetailRoute = () => {
</Stack> </Stack>
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<section> <section>
<Group <Group gap="sm" justify="space-between">
gap="sm"
justify="space-between"
>
<Group> <Group>
<PlayButton onClick={() => handlePlay()} /> <PlayButton onClick={() => handlePlay()} />
<ActionIcon <ActionIcon
@ -231,11 +228,7 @@ const DummyAlbumDetailRoute = () => {
<section> <section>
<Center> <Center>
<Group mr={5}> <Group mr={5}>
<Icon <Icon fill="error" icon="error" size={30} />
fill="error"
icon="error"
size={30}
/>
</Group> </Group>
<h2>{t('error.badAlbum', { postProcess: 'sentenceCase' })}</h2> <h2>{t('error.badAlbum', { postProcess: 'sentenceCase' })}</h2>
</Center> </Center>

View file

@ -202,10 +202,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
order: itemOrder.recentAlbums, order: itemOrder.recentAlbums,
title: ( title: (
<Group align="flex-end"> <Group align="flex-end">
<TextTitle <TextTitle fw={700} order={2}>
fw={700}
order={2}
>
{t('page.albumArtistDetail.recentReleases', { {t('page.albumArtistDetail.recentReleases', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
})} })}
@ -232,10 +229,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
loading: compilationAlbumsQuery?.isLoading || compilationAlbumsQuery.isFetching, loading: compilationAlbumsQuery?.isLoading || compilationAlbumsQuery.isFetching,
order: itemOrder.compilations, order: itemOrder.compilations,
title: ( title: (
<TextTitle <TextTitle fw={700} order={2}>
fw={700}
order={2}
>
{t('page.albumArtistDetail.appearsOn', { postProcess: 'sentenceCase' })} {t('page.albumArtistDetail.appearsOn', { postProcess: 'sentenceCase' })}
</TextTitle> </TextTitle>
), ),
@ -247,10 +241,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
itemType: LibraryItem.ALBUM_ARTIST, itemType: LibraryItem.ALBUM_ARTIST,
order: itemOrder.similarArtists, order: itemOrder.similarArtists,
title: ( title: (
<TextTitle <TextTitle fw={700} order={2}>
fw={700}
order={2}
>
{t('page.albumArtistDetail.relatedArtists', { {t('page.albumArtistDetail.relatedArtists', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
})} })}
@ -355,19 +346,10 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
detailQuery?.isLoading || detailQuery?.isLoading ||
(server?.type === ServerType.NAVIDROME && enabledItem.topSongs && topSongsQuery?.isLoading); (server?.type === ServerType.NAVIDROME && enabledItem.topSongs && topSongsQuery?.isLoading);
if (isLoading) if (isLoading) return <div className={styles.contentContainer} ref={cq.ref} />;
return (
<div
className={styles.contentContainer}
ref={cq.ref}
/>
);
return ( return (
<div <div className={styles.contentContainer} ref={cq.ref}>
className={styles.contentContainer}
ref={cq.ref}
>
<LibraryBackgroundOverlay backgroundColor={background} /> <LibraryBackgroundOverlay backgroundColor={background} />
<div className={styles.detailContainer}> <div className={styles.detailContainer}>
<Group gap="md"> <Group gap="md">
@ -481,15 +463,9 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
) : null} ) : null}
<Grid gutter="xl"> <Grid gutter="xl">
{biography ? ( {biography ? (
<Grid.Col <Grid.Col order={itemOrder.biography} span={12}>
order={itemOrder.biography}
span={12}
>
<section style={{ maxWidth: '1280px' }}> <section style={{ maxWidth: '1280px' }}>
<TextTitle <TextTitle fw={700} order={2}>
fw={700}
order={2}
>
{t('page.albumArtistDetail.about', { {t('page.albumArtistDetail.about', {
artist: detailQuery?.data?.name, artist: detailQuery?.data?.name,
})} })}
@ -499,23 +475,11 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
</Grid.Col> </Grid.Col>
) : null} ) : null}
{showTopSongs ? ( {showTopSongs ? (
<Grid.Col <Grid.Col order={itemOrder.topSongs} span={12}>
order={itemOrder.topSongs}
span={12}
>
<section> <section>
<Group <Group justify="space-between" wrap="nowrap">
justify="space-between" <Group align="flex-end" wrap="nowrap">
wrap="nowrap" <TextTitle fw={700} order={2}>
>
<Group
align="flex-end"
wrap="nowrap"
>
<TextTitle
fw={700}
order={2}
>
{t('page.albumArtistDetail.topSongs', { {t('page.albumArtistDetail.topSongs', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
})} })}

View file

@ -42,15 +42,9 @@ export const AlbumArtistListContent = ({
return ( return (
<Suspense fallback={<Spinner container />}> <Suspense fallback={<Spinner container />}>
{isGrid ? ( {isGrid ? (
<AlbumArtistListGridView <AlbumArtistListGridView gridRef={gridRef} itemCount={itemCount} />
gridRef={gridRef}
itemCount={itemCount}
/>
) : ( ) : (
<AlbumArtistListTableView <AlbumArtistListTableView itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
)} )}
</Suspense> </Suspense>
); );

View file

@ -372,11 +372,7 @@ export const AlbumArtistListHeaderFilters = ({
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button variant="subtle">{sortByLabel}</Button> <Button variant="subtle">{sortByLabel}</Button>
@ -395,10 +391,7 @@ export const AlbumArtistListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<OrderToggleButton <OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
onToggle={handleToggleSortOrder}
sortOrder={filter.sortOrder}
/>
{server?.type === ServerType.JELLYFIN && ( {server?.type === ServerType.JELLYFIN && (
<> <>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
@ -437,10 +430,7 @@ export const AlbumArtistListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group <Group gap="sm" wrap="nowrap">
gap="sm"
wrap="nowrap"
>
<ListConfigMenu <ListConfigMenu
autoFitColumns={table.autoFit} autoFitColumns={table.autoFit}
disabledViewTypes={[ListDisplayType.LIST]} disabledViewTypes={[ListDisplayType.LIST]}

View file

@ -46,15 +46,9 @@ export const AlbumArtistListHeader = ({
}, 500); }, 500);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader> <PageHeader>
<Flex <Flex justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.Title> <LibraryHeaderBar.Title>
{t('page.albumArtistList.title', { postProcess: 'titleCase' })} {t('page.albumArtistList.title', { postProcess: 'titleCase' })}
@ -66,18 +60,12 @@ export const AlbumArtistListHeader = ({
</LibraryHeaderBar.Badge> </LibraryHeaderBar.Badge>
</LibraryHeaderBar> </LibraryHeaderBar>
<Group> <Group>
<SearchInput <SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
defaultValue={filter.searchTerm}
onChange={handleSearch}
/>
</Group> </Group>
</Flex> </Flex>
</PageHeader> </PageHeader>
<FilterBar> <FilterBar>
<AlbumArtistListHeaderFilters <AlbumArtistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
gridRef={gridRef}
tableRef={tableRef}
/>
</FilterBar> </FilterBar>
</Stack> </Stack>
); );

View file

@ -34,15 +34,9 @@ export const ArtistListContent = ({ gridRef, itemCount, tableRef }: ArtistListCo
return ( return (
<Suspense fallback={<Spinner container />}> <Suspense fallback={<Spinner container />}>
{isGrid ? ( {isGrid ? (
<ArtistListGridView <ArtistListGridView gridRef={gridRef} itemCount={itemCount} />
gridRef={gridRef}
itemCount={itemCount}
/>
) : ( ) : (
<ArtistListTableView <ArtistListTableView itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
)} )}
</Suspense> </Suspense>
); );

View file

@ -388,11 +388,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button variant="subtle">{sortByLabel}</Button> <Button variant="subtle">{sortByLabel}</Button>
@ -411,19 +407,13 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<OrderToggleButton <OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
onToggle={handleToggleSortOrder}
sortOrder={filter.sortOrder}
/>
{server?.type === ServerType.JELLYFIN && ( {server?.type === ServerType.JELLYFIN && (
<> <>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<ActionIcon <ActionIcon icon="folder" variant="subtle" />
icon="folder"
variant="subtle"
/>
</DropdownMenu.Target> </DropdownMenu.Target>
<DropdownMenu.Dropdown> <DropdownMenu.Dropdown>
{musicFoldersQuery.data?.items.map((folder) => ( {musicFoldersQuery.data?.items.map((folder) => (
@ -442,11 +432,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
)} )}
{roles.data?.length && ( {roles.data?.length && (
<> <>
<Select <Select data={roles.data} onChange={handleSetRole} value={filter.role} />
data={roles.data}
onChange={handleSetRole}
value={filter.role}
/>
</> </>
)} )}
<RefreshButton onClick={handleRefresh} /> <RefreshButton onClick={handleRefresh} />
@ -466,10 +452,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group <Group gap="xs" wrap="nowrap">
gap="xs"
wrap="nowrap"
>
<ListConfigMenu <ListConfigMenu
autoFitColumns={table.autoFit} autoFitColumns={table.autoFit}
displayType={display} displayType={display}

View file

@ -42,15 +42,9 @@ export const ArtistListHeader = ({ gridRef, itemCount, tableRef }: ArtistListHea
}, 500); }, 500);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader> <PageHeader>
<Flex <Flex justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.Title> <LibraryHeaderBar.Title>
{t('entity.artist_other', { postProcess: 'titleCase' })} {t('entity.artist_other', { postProcess: 'titleCase' })}
@ -62,18 +56,12 @@ export const ArtistListHeader = ({ gridRef, itemCount, tableRef }: ArtistListHea
</LibraryHeaderBar.Badge> </LibraryHeaderBar.Badge>
</LibraryHeaderBar> </LibraryHeaderBar>
<Group> <Group>
<SearchInput <SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
defaultValue={filter.searchTerm}
onChange={handleSearch}
/>
</Group> </Group>
</Flex> </Flex>
</PageHeader> </PageHeader>
<FilterBar> <FilterBar>
<ArtistListHeaderFilters <ArtistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
gridRef={gridRef}
tableRef={tableRef}
/>
</FilterBar> </FilterBar>
</Stack> </Stack>
); );

View file

@ -41,16 +41,8 @@ const ArtistListRoute = () => {
return ( return (
<AnimatedPage> <AnimatedPage>
<ListContext.Provider value={providerValue}> <ListContext.Provider value={providerValue}>
<ArtistListHeader <ArtistListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
gridRef={gridRef} <ArtistListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
<ArtistListContent
gridRef={gridRef}
itemCount={itemCount}
tableRef={tableRef}
/>
</ListContext.Provider> </ListContext.Provider>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -533,10 +533,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
openModal({ openModal({
children: ( children: (
<ConfirmModal <ConfirmModal loading={removeFromPlaylistMutation.isLoading} onConfirm={confirm}>
loading={removeFromPlaylistMutation.isLoading}
onConfirm={confirm}
>
{t('common.areYouSure', { postProcess: 'sentenceCase' })} {t('common.areYouSure', { postProcess: 'sentenceCase' })}
</ConfirmModal> </ConfirmModal>
), ),
@ -922,26 +919,15 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
<Portal> <Portal>
<AnimatePresence> <AnimatePresence>
{opened && ( {opened && (
<ContextMenu <ContextMenu minWidth={125} ref={mergedRef} xPos={ctx.xPos} yPos={ctx.yPos}>
minWidth={125}
ref={mergedRef}
xPos={ctx.xPos}
yPos={ctx.yPos}
>
<Stack gap={0}> <Stack gap={0}>
<Stack <Stack gap={0} onClick={closeContextMenu}>
gap={0}
onClick={closeContextMenu}
>
{ctx.menuItems?.map((item) => { {ctx.menuItems?.map((item) => {
return ( return (
!contextMenuItems[item.id].disabled && ( !contextMenuItems[item.id].disabled && (
<Fragment key={`context-menu-${item.id}`}> <Fragment key={`context-menu-${item.id}`}>
{item.children ? ( {item.children ? (
<HoverCard <HoverCard offset={0} position="right">
offset={0}
position="right"
>
<HoverCard.Target> <HoverCard.Target>
<ContextMenuButton <ContextMenuButton
leftIcon={ leftIcon={

View file

@ -33,15 +33,9 @@ export const GenreListContent = ({ gridRef, itemCount, tableRef }: AlbumListCont
return ( return (
<Suspense fallback={<Spinner container />}> <Suspense fallback={<Spinner container />}>
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? ( {display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
<GenreListGridView <GenreListGridView gridRef={gridRef} itemCount={itemCount} />
gridRef={gridRef}
itemCount={itemCount}
/>
) : ( ) : (
<GenreListTableView <GenreListTableView itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
)} )}
</Suspense> </Suspense>
); );

View file

@ -254,11 +254,7 @@ export const GenreListHeaderFilters = ({
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button variant="subtle">{sortByLabel}</Button> <Button variant="subtle">{sortByLabel}</Button>
@ -277,10 +273,7 @@ export const GenreListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<OrderToggleButton <OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
onToggle={handleToggleSortOrder}
sortOrder={filter.sortOrder}
/>
{server?.type === ServerType.JELLYFIN && ( {server?.type === ServerType.JELLYFIN && (
<> <>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
@ -340,10 +333,7 @@ export const GenreListHeaderFilters = ({
</Button> </Button>
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group <Group gap="sm" wrap="nowrap">
gap="sm"
wrap="nowrap"
>
<ListConfigMenu <ListConfigMenu
autoFitColumns={table.autoFit} autoFitColumns={table.autoFit}
disabledViewTypes={[ListDisplayType.LIST]} disabledViewTypes={[ListDisplayType.LIST]}

View file

@ -40,15 +40,9 @@ export const GenreListHeader = ({ gridRef, itemCount, tableRef }: GenreListHeade
}, 500); }, 500);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader> <PageHeader>
<Flex <Flex justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.Title> <LibraryHeaderBar.Title>
{t('page.genreList.title', { postProcess: 'titleCase' })} {t('page.genreList.title', { postProcess: 'titleCase' })}
@ -60,10 +54,7 @@ export const GenreListHeader = ({ gridRef, itemCount, tableRef }: GenreListHeade
</LibraryHeaderBar.Badge> </LibraryHeaderBar.Badge>
</LibraryHeaderBar> </LibraryHeaderBar>
<Group> <Group>
<SearchInput <SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
defaultValue={filter.searchTerm}
onChange={handleSearch}
/>
</Group> </Group>
</Flex> </Flex>
</PageHeader> </PageHeader>

View file

@ -42,16 +42,8 @@ const GenreListRoute = () => {
return ( return (
<AnimatedPage> <AnimatedPage>
<ListContext.Provider value={providerValue}> <ListContext.Provider value={providerValue}>
<GenreListHeader <GenreListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
gridRef={gridRef} <GenreListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
<GenreListContent
gridRef={gridRef}
itemCount={itemCount}
tableRef={tableRef}
/>
</ListContext.Provider> </ListContext.Provider>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -81,10 +81,7 @@ const formatArtists = (artists: null | RelatedArtist[] | undefined) =>
{artist.name || '—'} {artist.name || '—'}
</Text> </Text>
) : ( ) : (
<Text <Text overflow="visible" size="md">
overflow="visible"
size="md"
>
{artist.name || '-'} {artist.name || '-'}
</Text> </Text>
)} )}
@ -119,17 +116,7 @@ const FormatGenre = (item: Album | AlbumArtist | Playlist | Song) => {
}; };
const BoolField = (key: boolean) => const BoolField = (key: boolean) =>
key ? ( key ? <Icon color="success" icon="check" /> : <Icon color="error" icon="x" />;
<Icon
color="success"
icon="check"
/>
) : (
<Icon
color="error"
icon="x"
/>
);
const AlbumPropertyMapping: ItemDetailRow<Album>[] = [ const AlbumPropertyMapping: ItemDetailRow<Album>[] = [
{ key: 'name', label: 'common.title' }, { key: 'name', label: 'common.title' },
@ -409,12 +396,7 @@ export const ItemDetailsModal = ({ item }: ItemDetailsModalProps) => {
} }
return ( return (
<Table <Table highlightOnHover variant="vertical" withRowBorders={false} withTableBorder>
highlightOnHover
variant="vertical"
withRowBorders={false}
withTableBorder
>
<Table.Tbody>{body}</Table.Tbody> <Table.Tbody>{body}</Table.Tbody>
</Table> </Table>
); );

View file

@ -22,10 +22,7 @@ export const SongPath = ({ path }: SongPathProps) => {
return ( return (
<Group> <Group>
<CopyButton <CopyButton timeout={2000} value={path}>
timeout={2000}
value={path}
>
{({ copied, copy }) => ( {({ copied, copy }) => (
<Tooltip <Tooltip
label={t( label={t(
@ -36,10 +33,7 @@ export const SongPath = ({ path }: SongPathProps) => {
)} )}
withinPortal withinPortal
> >
<ActionIcon <ActionIcon onClick={copy} variant="transparent">
onClick={copy}
variant="transparent"
>
{copied ? <Icon icon="check" /> : <Icon icon="clipboardCopy" />} {copied ? <Icon icon="check" /> : <Icon icon="clipboardCopy" />}
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>

View file

@ -38,33 +38,15 @@ const SearchResult = ({ data, onClick }: SearchResultProps) => {
source === LyricSource.GENIUS ? id.replace(/^((http[s]?|ftp):\/)?\/?([^:/\s]+)/g, '') : id; source === LyricSource.GENIUS ? id.replace(/^((http[s]?|ftp):\/)?\/?([^:/\s]+)/g, '') : id;
return ( return (
<button <button className={styles.searchItem} onClick={onClick}>
className={styles.searchItem} <Group justify="space-between" wrap="nowrap">
onClick={onClick} <Stack gap={0} maw="65%">
> <Text fw={600} size="md">
<Group
justify="space-between"
wrap="nowrap"
>
<Stack
gap={0}
maw="65%"
>
<Text
fw={600}
size="md"
>
{name} {name}
</Text> </Text>
<Text isMuted>{artist}</Text> <Text isMuted>{artist}</Text>
<Group <Group gap="sm" wrap="nowrap">
gap="sm" <Text isMuted size="sm">
wrap="nowrap"
>
<Text
isMuted
size="sm"
>
{[source, cleanId].join(' — ')} {[source, cleanId].join(' — ')}
</Text> </Text>
</Group> </Group>
@ -167,11 +149,7 @@ export const LyricsSearchForm = ({ artist, name, onSearchOverride }: LyricSearch
export const openLyricSearchModal = ({ artist, name, onSearchOverride }: LyricSearchFormProps) => { export const openLyricSearchModal = ({ artist, name, onSearchOverride }: LyricSearchFormProps) => {
openModal({ openModal({
children: ( children: (
<LyricsSearchForm <LyricsSearchForm artist={artist} name={name} onSearchOverride={onSearchOverride} />
artist={artist}
name={name}
onSearchOverride={onSearchOverride}
/>
), ),
size: 'lg', size: 'lg',
title: i18n.t('form.lyricSearch.title', { postProcess: 'titleCase' }) as string, title: i18n.t('form.lyricSearch.title', { postProcess: 'titleCase' }) as string,

View file

@ -151,10 +151,7 @@ export const Lyrics = () => {
<ErrorBoundary FallbackComponent={ErrorFallback}> <ErrorBoundary FallbackComponent={ErrorFallback}>
<div className={styles.lyricsContainer}> <div className={styles.lyricsContainer}>
{isLoadingLyrics ? ( {isLoadingLyrics ? (
<Spinner <Spinner container size={25} />
container
size={25}
/>
) : ( ) : (
<AnimatePresence mode="sync"> <AnimatePresence mode="sync">
{hasNoLyrics ? ( {hasNoLyrics ? (

View file

@ -29,10 +29,7 @@ export const UnsynchronizedLyrics = ({
}, [translatedLyrics]); }, [translatedLyrics]);
return ( return (
<div <div className={styles.container} style={{ gap: `${settings.gapUnsync}px` }}>
className={styles.container}
style={{ gap: `${settings.gapUnsync}px` }}
>
{settings.showProvider && source && ( {settings.showProvider && source && (
<LyricLine <LyricLine
alignment={settings.alignment} alignment={settings.alignment}

View file

@ -11,30 +11,17 @@ export const DrawerPlayQueue = () => {
const queueRef = useRef<null | { grid: AgGridReactType<Song> }>(null); const queueRef = useRef<null | { grid: AgGridReactType<Song> }>(null);
return ( return (
<Flex <Flex direction="column" h="100%">
direction="column"
h="100%"
>
<div <div
style={{ style={{
backgroundColor: 'var(--theme-colors-background)', backgroundColor: 'var(--theme-colors-background)',
borderRadius: '10px', borderRadius: '10px',
}} }}
> >
<PlayQueueListControls <PlayQueueListControls tableRef={queueRef} type="sideQueue" />
tableRef={queueRef}
type="sideQueue"
/>
</div> </div>
<Flex <Flex bg="var(--theme-colors-background)" h="100%" mb="0.6rem">
bg="var(--theme-colors-background)" <PlayQueue ref={queueRef} type="sideQueue" />
h="100%"
mb="0.6rem"
>
<PlayQueue
ref={queueRef}
type="sideQueue"
/>
</Flex> </Flex>
</Flex> </Flex>
); );

View file

@ -174,10 +174,7 @@ export const PlayQueueListControls = ({ tableRef, type }: PlayQueueListOptionsPr
/> />
</Group> </Group>
<Group> <Group>
<Popover <Popover position="top-end" transitionProps={{ transition: 'fade' }}>
position="top-end"
transitionProps={{ transition: 'fade' }}
>
<Popover.Target> <Popover.Target>
<ActionIcon <ActionIcon
icon="settings" icon="settings"

View file

@ -18,19 +18,10 @@ export const SidebarPlayQueue = () => {
const isWeb = windowBarStyle === Platform.WEB; const isWeb = windowBarStyle === Platform.WEB;
return ( return (
<VirtualGridContainer> <VirtualGridContainer>
<Box <Box display={!isWeb ? 'flex' : undefined} h="65px">
display={!isWeb ? 'flex' : undefined} <PlayQueueListControls tableRef={queueRef} type="sideQueue" />
h="65px"
>
<PlayQueueListControls
tableRef={queueRef}
type="sideQueue"
/>
</Box> </Box>
<PlayQueue <PlayQueue ref={queueRef} type="sideQueue" />
ref={queueRef}
type="sideQueue"
/>
</VirtualGridContainer> </VirtualGridContainer>
); );
}; };

View file

@ -16,14 +16,8 @@ const NowPlayingRoute = () => {
<AnimatedPage> <AnimatedPage>
<VirtualGridContainer> <VirtualGridContainer>
<NowPlayingHeader /> <NowPlayingHeader />
<PlayQueueListControls <PlayQueueListControls tableRef={queueRef} type="nowPlaying" />
tableRef={queueRef} <PlayQueue ref={queueRef} type="nowPlaying" />
type="nowPlaying"
/>
<PlayQueue
ref={queueRef}
type="nowPlaying"
/>
</VirtualGridContainer> </VirtualGridContainer>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -115,13 +115,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
<div className={styles.controlsContainer}> <div className={styles.controlsContainer}>
<div className={styles.buttonsContainer}> <div className={styles.buttonsContainer}>
<PlayerButton <PlayerButton
icon={ icon={<Icon fill="default" icon="mediaStop" size={buttonSize - 2} />}
<Icon
fill="default"
icon="mediaStop"
size={buttonSize - 2}
/>
}
onClick={handleStop} onClick={handleStop}
tooltip={{ tooltip={{
label: t('player.stop', { postProcess: 'sentenceCase' }), label: t('player.stop', { postProcess: 'sentenceCase' }),
@ -152,13 +146,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
variant="tertiary" variant="tertiary"
/> />
<PlayerButton <PlayerButton
icon={ icon={<Icon fill="default" icon="mediaPrevious" size={buttonSize} />}
<Icon
fill="default"
icon="mediaPrevious"
size={buttonSize}
/>
}
onClick={handlePrevTrack} onClick={handlePrevTrack}
tooltip={{ tooltip={{
label: t('player.previous', { postProcess: 'sentenceCase' }), label: t('player.previous', { postProcess: 'sentenceCase' }),
@ -169,11 +157,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
{skip?.enabled && ( {skip?.enabled && (
<PlayerButton <PlayerButton
icon={ icon={
<Icon <Icon fill="default" icon="mediaStepBackward" size={buttonSize} />
fill="default"
icon="mediaStepBackward"
size={buttonSize}
/>
} }
onClick={() => handleSkipBackward(skip?.skipBackwardSeconds)} onClick={() => handleSkipBackward(skip?.skipBackwardSeconds)}
tooltip={{ tooltip={{
@ -194,13 +178,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/> />
{skip?.enabled && ( {skip?.enabled && (
<PlayerButton <PlayerButton
icon={ icon={<Icon fill="default" icon="mediaStepForward" size={buttonSize} />}
<Icon
fill="default"
icon="mediaStepForward"
size={buttonSize}
/>
}
onClick={() => handleSkipForward(skip?.skipForwardSeconds)} onClick={() => handleSkipForward(skip?.skipForwardSeconds)}
tooltip={{ tooltip={{
label: t('player.skip', { label: t('player.skip', {
@ -214,13 +192,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/> />
)} )}
<PlayerButton <PlayerButton
icon={ icon={<Icon fill="default" icon="mediaNext" size={buttonSize} />}
<Icon
fill="default"
icon="mediaNext"
size={buttonSize}
/>
}
onClick={handleNextTrack} onClick={handleNextTrack}
tooltip={{ tooltip={{
label: t('player.next', { postProcess: 'sentenceCase' }), label: t('player.next', { postProcess: 'sentenceCase' }),
@ -231,11 +203,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
<PlayerButton <PlayerButton
icon={ icon={
repeat === PlayerRepeat.ONE ? ( repeat === PlayerRepeat.ONE ? (
<Icon <Icon fill="primary" icon="mediaRepeatOne" size={buttonSize} />
fill="primary"
icon="mediaRepeatOne"
size={buttonSize}
/>
) : ( ) : (
<Icon <Icon
fill={repeat === PlayerRepeat.NONE ? 'default' : 'primary'} fill={repeat === PlayerRepeat.NONE ? 'default' : 'primary'}
@ -268,13 +236,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
variant="tertiary" variant="tertiary"
/> />
<PlayerButton <PlayerButton
icon={ icon={<Icon fill="default" icon="mediaRandom" size={buttonSize} />}
<Icon
fill="default"
icon="mediaRandom"
size={buttonSize}
/>
}
onClick={() => onClick={() =>
openShuffleAllModal({ openShuffleAllModal({
handlePlayQueueAdd, handlePlayQueueAdd,
@ -291,12 +253,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
</div> </div>
<div className={styles.sliderContainer}> <div className={styles.sliderContainer}>
<div className={styles.sliderValueWrapper}> <div className={styles.sliderValueWrapper}>
<Text <Text fw={600} isMuted isNoSelect size="xs">
fw={600}
isMuted
isNoSelect
size="xs"
>
{formattedTime} {formattedTime}
</Text> </Text>
</div> </div>
@ -324,12 +281,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/> />
</div> </div>
<div className={styles.sliderValueWrapper}> <div className={styles.sliderValueWrapper}>
<Text <Text fw={600} isMuted isNoSelect size="xs">
fw={600}
isMuted
isNoSelect
size="xs"
>
{duration} {duration}
</Text> </Text>
</div> </div>

View file

@ -68,11 +68,7 @@ const ImageWithPlaceholder = ({
width: '100%', width: '100%',
}} }}
> >
<Icon <Icon color="muted" icon="itemAlbum" size="25%" />
color="muted"
icon="itemAlbum"
size="25%"
/>
</Center> </Center>
); );
} }
@ -167,14 +163,8 @@ export const FullScreenPlayerImage = () => {
justify="flex-start" justify="flex-start"
p="1rem" p="1rem"
> >
<div <div className={styles.imageContainer} ref={mainImageRef}>
className={styles.imageContainer} <AnimatePresence initial={false} mode="sync">
ref={mainImageRef}
>
<AnimatePresence
initial={false}
mode="sync"
>
{imageState.current === 0 && ( {imageState.current === 0 && (
<ImageWithPlaceholder <ImageWithPlaceholder
animate="open" animate="open"
@ -206,18 +196,8 @@ export const FullScreenPlayerImage = () => {
)} )}
</AnimatePresence> </AnimatePresence>
</div> </div>
<Stack <Stack className={styles.metadataContainer} gap="md" maw="100%">
className={styles.metadataContainer} <Text fw={900} lh="1.2" overflow="hidden" size="4xl" w="100%">
gap="md"
maw="100%"
>
<Text
fw={900}
lh="1.2"
overflow="hidden"
size="4xl"
w="100%"
>
{currentSong?.name} {currentSong?.name}
</Text> </Text>
<Text <Text
@ -257,10 +237,7 @@ export const FullScreenPlayerImage = () => {
</Fragment> </Fragment>
))} ))}
</Text> </Text>
<Group <Group justify="center" mt="sm">
justify="center"
mt="sm"
>
{currentSong?.container && ( {currentSong?.container && (
<Badge variant="transparent">{currentSong?.container}</Badge> <Badge variant="transparent">{currentSong?.container}</Badge>
)} )}

View file

@ -76,10 +76,7 @@ export const FullScreenPlayerQueue = () => {
justify="center" justify="center"
> >
{headerItems.map((item) => ( {headerItems.map((item) => (
<div <div className={styles.headerItemWrapper} key={`tab-${item.label}`}>
className={styles.headerItemWrapper}
key={`tab-${item.label}`}
>
<Button <Button
flex={1} flex={1}
fw="600" fw="600"

View file

@ -238,10 +238,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
})} })}
</Option.Label> </Option.Label>
<Option.Control> <Option.Control>
<Group <Group w="100%" wrap="nowrap">
w="100%"
wrap="nowrap"
>
<Slider <Slider
defaultValue={lyricConfig.fontSize} defaultValue={lyricConfig.fontSize}
label={(e) => label={(e) =>
@ -278,10 +275,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
})} })}
</Option.Label> </Option.Label>
<Option.Control> <Option.Control>
<Group <Group w="100%" wrap="nowrap">
w="100%"
wrap="nowrap"
>
<Slider <Slider
defaultValue={lyricConfig.gap} defaultValue={lyricConfig.gap}
label={(e) => `Synchronized: ${e}px`} label={(e) => `Synchronized: ${e}px`}

View file

@ -4,10 +4,5 @@ import { useCurrentSong } from '/@/renderer/store';
export const FullScreenSimilarSongs = () => { export const FullScreenSimilarSongs = () => {
const currentSong = useCurrentSong(); const currentSong = useCurrentSong();
return currentSong?.id ? ( return currentSong?.id ? <SimilarSongsList fullScreen song={currentSong} /> : null;
<SimilarSongsList
fullScreen
song={currentSong}
/>
) : null;
}; };

View file

@ -69,10 +69,7 @@ export const LeftControls = () => {
return ( return (
<div className={styles.leftControlsContainer}> <div className={styles.leftControlsContainer}>
<LayoutGroup> <LayoutGroup>
<AnimatePresence <AnimatePresence initial={false} mode="popLayout">
initial={false}
mode="popLayout"
>
{!hideImage && ( {!hideImage && (
<div className={styles.imageWrapper}> <div className={styles.imageWrapper}>
<motion.div <motion.div
@ -123,19 +120,9 @@ export const LeftControls = () => {
</div> </div>
)} )}
</AnimatePresence> </AnimatePresence>
<motion.div <motion.div className={styles.metadataStack} layout="position">
className={styles.metadataStack} <div className={styles.lineItem} onClick={stopPropagation}>
layout="position" <Group align="center" gap="xs" wrap="nowrap">
>
<div
className={styles.lineItem}
onClick={stopPropagation}
>
<Group
align="center"
gap="xs"
wrap="nowrap"
>
<Text <Text
component={Link} component={Link}
fw={500} fw={500}

View file

@ -193,13 +193,7 @@ export const RightControls = () => {
}, [addToFavoritesMutation, removeFromFavoritesMutation, updateRatingMutation]); }, [addToFavoritesMutation, removeFromFavoritesMutation, updateRatingMutation]);
return ( return (
<Flex <Flex align="flex-end" direction="column" h="100%" px="1rem" py="0.5rem">
align="flex-end"
direction="column"
h="100%"
px="1rem"
py="0.5rem"
>
<Group h="calc(100% / 3)"> <Group h="calc(100% / 3)">
{showRating && ( {showRating && (
<Rating <Rating
@ -209,18 +203,8 @@ export const RightControls = () => {
/> />
)} )}
</Group> </Group>
<Group <Group align="center" gap="xs" wrap="nowrap">
align="center" <DropdownMenu arrowOffset={12} offset={0} position="top-end" width={425} withArrow>
gap="xs"
wrap="nowrap"
>
<DropdownMenu
arrowOffset={12}
offset={0}
position="top-end"
width={425}
withArrow
>
<DropdownMenu.Target> <DropdownMenu.Target>
<ActionIcon <ActionIcon
icon="mediaSpeed" icon="mediaSpeed"

View file

@ -33,10 +33,5 @@ export const Visualizer = () => {
return () => {}; return () => {};
}, [accent, canvasRef, motion, webAudio]); }, [accent, canvasRef, motion, webAudio]);
return ( return <div className={styles.container} ref={canvasRef} />;
<div
className={styles.container}
ref={canvasRef}
/>
);
}; };

View file

@ -155,10 +155,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
)} )}
<Group justify="flex-end"> <Group justify="flex-end">
<Button <Button onClick={onCancel} variant="subtle">
onClick={onCancel}
variant="subtle"
>
{t('common.cancel', { postProcess: 'titleCase' })} {t('common.cancel', { postProcess: 'titleCase' })}
</Button> </Button>
<Button <Button

View file

@ -331,11 +331,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
/> />
</VirtualGridAutoSizerContainer> </VirtualGridAutoSizerContainer>
{isPaginationEnabled && ( {isPaginationEnabled && (
<AnimatePresence <AnimatePresence initial={false} mode="wait" presenceAffectsLayout>
initial={false}
mode="wait"
presenceAffectsLayout
>
{page.display === ListDisplayType.TABLE_PAGINATED && ( {page.display === ListDisplayType.TABLE_PAGINATED && (
<TablePagination <TablePagination
pageKey={playlistId} pageKey={playlistId}

View file

@ -469,11 +469,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button <Button
@ -555,10 +551,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
{server?.type === ServerType.NAVIDROME && !isSmartPlaylist && ( {server?.type === ServerType.NAVIDROME && !isSmartPlaylist && (
<> <>
<DropdownMenu.Divider /> <DropdownMenu.Divider />
<DropdownMenu.Item <DropdownMenu.Item isDanger onClick={handleToggleShowQueryBuilder}>
isDanger
onClick={handleToggleShowQueryBuilder}
>
{t('action.toggleSmartPlaylistEditor', { {t('action.toggleSmartPlaylistEditor', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
})} })}

View file

@ -33,15 +33,9 @@ export const PlaylistListContent = ({ gridRef, itemCount, tableRef }: PlaylistLi
return ( return (
<Suspense fallback={<Spinner container />}> <Suspense fallback={<Spinner container />}>
{display === ListDisplayType.CARD || display === ListDisplayType.GRID ? ( {display === ListDisplayType.CARD || display === ListDisplayType.GRID ? (
<PlaylistListGridView <PlaylistListGridView gridRef={gridRef} itemCount={itemCount} />
gridRef={gridRef}
itemCount={itemCount}
/>
) : ( ) : (
<PlaylistListTableView <PlaylistListTableView itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
)} )}
<div /> <div />
</Suspense> </Suspense>

View file

@ -355,11 +355,7 @@ export const PlaylistListHeaderFilters = ({
return ( return (
<Flex justify="space-between"> <Flex justify="space-between">
<Group <Group gap="sm" ref={cq.ref} w="100%">
gap="sm"
ref={cq.ref}
w="100%"
>
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
<Button variant="subtle">{sortByLabel}</Button> <Button variant="subtle">{sortByLabel}</Button>
@ -378,10 +374,7 @@ export const PlaylistListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
<Divider orientation="vertical" /> <Divider orientation="vertical" />
<OrderToggleButton <OrderToggleButton onToggle={handleToggleSortOrder} sortOrder={filter.sortOrder} />
onToggle={handleToggleSortOrder}
sortOrder={filter.sortOrder}
/>
<RefreshButton onClick={handleRefresh} /> <RefreshButton onClick={handleRefresh} />
<DropdownMenu position="bottom-start"> <DropdownMenu position="bottom-start">
<DropdownMenu.Target> <DropdownMenu.Target>
@ -397,14 +390,8 @@ export const PlaylistListHeaderFilters = ({
</DropdownMenu.Dropdown> </DropdownMenu.Dropdown>
</DropdownMenu> </DropdownMenu>
</Group> </Group>
<Group <Group gap="xs" wrap="nowrap">
gap="xs" <Button onClick={handleCreatePlaylistModal} variant="subtle">
wrap="nowrap"
>
<Button
onClick={handleCreatePlaylistModal}
variant="subtle"
>
{t('action.createPlaylist', { postProcess: 'sentenceCase' })} {t('action.createPlaylist', { postProcess: 'sentenceCase' })}
</Button> </Button>
<ListConfigMenu <ListConfigMenu

View file

@ -44,16 +44,9 @@ export const PlaylistListHeader = ({ gridRef, itemCount, tableRef }: PlaylistLis
}, 500); }, 500);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader> <PageHeader>
<Flex <Flex align="center" justify="space-between" w="100%">
align="center"
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.Title> <LibraryHeaderBar.Title>
{t('page.playlistList.title', { postProcess: 'titleCase' })} {t('page.playlistList.title', { postProcess: 'titleCase' })}
@ -67,18 +60,12 @@ export const PlaylistListHeader = ({ gridRef, itemCount, tableRef }: PlaylistLis
</Badge> </Badge>
</LibraryHeaderBar> </LibraryHeaderBar>
<Group> <Group>
<SearchInput <SearchInput defaultValue={filter.searchTerm} onChange={handleSearch} />
defaultValue={filter.searchTerm}
onChange={handleSearch}
/>
</Group> </Group>
</Flex> </Flex>
</PageHeader> </PageHeader>
<FilterBar> <FilterBar>
<PlaylistListHeaderFilters <PlaylistListHeaderFilters gridRef={gridRef} tableRef={tableRef} />
gridRef={gridRef}
tableRef={tableRef}
/>
</FilterBar> </FilterBar>
</Stack> </Stack>
); );

View file

@ -410,11 +410,7 @@ export const PlaylistQueryBuilder = forwardRef(
]; ];
return ( return (
<Flex <Flex direction="column" h="calc(100% - 2rem)" justify="space-between">
direction="column"
h="calc(100% - 2rem)"
justify="space-between"
>
<ScrollArea> <ScrollArea>
<QueryBuilder <QueryBuilder
data={filters} data={filters}
@ -442,17 +438,8 @@ export const PlaylistQueryBuilder = forwardRef(
uniqueId={filters.uniqueId} uniqueId={filters.uniqueId}
/> />
</ScrollArea> </ScrollArea>
<Group <Group align="flex-end" justify="space-between" m="1rem" wrap="nowrap">
align="flex-end" <Group gap="sm" w="100%" wrap="nowrap">
justify="space-between"
m="1rem"
wrap="nowrap"
>
<Group
gap="sm"
w="100%"
wrap="nowrap"
>
<Select <Select
data={sortOptions} data={sortOptions}
label="Sort" label="Sort"
@ -485,20 +472,11 @@ export const PlaylistQueryBuilder = forwardRef(
/> />
</Group> </Group>
{onSave && onSaveAs && ( {onSave && onSaveAs && (
<Group <Group gap="sm" wrap="nowrap">
gap="sm" <Button loading={isSaving} onClick={handleSaveAs}>
wrap="nowrap"
>
<Button
loading={isSaving}
onClick={handleSaveAs}
>
{t('common.saveAs', { postProcess: 'titleCase' })} {t('common.saveAs', { postProcess: 'titleCase' })}
</Button> </Button>
<Button <Button onClick={openPreviewModal} variant="subtle">
onClick={openPreviewModal}
variant="subtle"
>
{t('common.preview', { postProcess: 'titleCase' })} {t('common.preview', { postProcess: 'titleCase' })}
</Button> </Button>
<DropdownMenu position="bottom-end"> <DropdownMenu position="bottom-end">
@ -512,12 +490,7 @@ export const PlaylistQueryBuilder = forwardRef(
<DropdownMenu.Dropdown> <DropdownMenu.Dropdown>
<DropdownMenu.Item <DropdownMenu.Item
isDanger isDanger
leftSection={ leftSection={<Icon color="error" icon="save" />}
<Icon
color="error"
icon="save"
/>
}
onClick={handleSave} onClick={handleSave}
> >
{t('common.saveAndReplace', { postProcess: 'titleCase' })} {t('common.saveAndReplace', { postProcess: 'titleCase' })}

View file

@ -103,10 +103,7 @@ export const SaveAsPlaylistForm = ({
/> />
)} )}
<Group justify="flex-end"> <Group justify="flex-end">
<Button <Button onClick={onCancel} variant="subtle">
onClick={onCancel}
variant="subtle"
>
{t('common.cancel', { postProcess: 'titleCase' })} {t('common.cancel', { postProcess: 'titleCase' })}
</Button> </Button>
<Button <Button

View file

@ -140,10 +140,7 @@ export const UpdatePlaylistForm = ({ body, onCancel, query, users }: UpdatePlayl
</> </>
)} )}
<Group justify="flex-end"> <Group justify="flex-end">
<Button <Button onClick={onCancel} variant="subtle">
onClick={onCancel}
variant="subtle"
>
{t('common.cancel', { postProcess: 'titleCase' })} {t('common.cancel', { postProcess: 'titleCase' })}
</Button> </Button>
<Button <Button

View file

@ -175,12 +175,7 @@ const PlaylistDetailSongListRoute = () => {
{(isSmartPlaylist || showQueryBuilder) && ( {(isSmartPlaylist || showQueryBuilder) && (
<motion.div> <motion.div>
<Box <Box h="100%" mah="35vh" p="md" w="100%">
h="100%"
mah="35vh"
p="md"
w="100%"
>
<Group pb="md"> <Group pb="md">
<ActionIcon <ActionIcon
icon={isQueryBuilderExpanded ? 'arrowUpS' : 'arrowDownS'} icon={isQueryBuilderExpanded ? 'arrowUpS' : 'arrowDownS'}

View file

@ -50,16 +50,8 @@ const PlaylistListRoute = () => {
return ( return (
<AnimatedPage> <AnimatedPage>
<ListContext.Provider value={providerValue}> <ListContext.Provider value={providerValue}>
<PlaylistListHeader <PlaylistListHeader gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
gridRef={gridRef} <PlaylistListContent gridRef={gridRef} itemCount={itemCount} tableRef={tableRef} />
itemCount={itemCount}
tableRef={tableRef}
/>
<PlaylistListContent
gridRef={gridRef}
itemCount={itemCount}
tableRef={tableRef}
/>
</ListContext.Provider> </ListContext.Provider>
</AnimatedPage> </AnimatedPage>
); );

View file

@ -95,18 +95,11 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
header: { display: 'none' }, header: { display: 'none' },
}} }}
> >
<Group <Group gap="sm" mb="1rem">
gap="sm"
mb="1rem"
>
{pages.map((page, index) => ( {pages.map((page, index) => (
<Fragment key={page}> <Fragment key={page}>
{index > 0 && ' > '} {index > 0 && ' > '}
<Button <Button disabled size="compact-md" variant="default">
disabled
size="compact-md"
variant="default"
>
{page?.toLocaleUpperCase()} {page?.toLocaleUpperCase()}
</Button> </Button>
</Fragment> </Fragment>
@ -267,10 +260,7 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => {
)} )}
</Command.List> </Command.List>
</Command> </Command>
<Box <Box mt="0.5rem" p="0.5rem">
mt="0.5rem"
p="0.5rem"
>
<Group justify="space-between"> <Group justify="space-between">
<Command.Loading> <Command.Loading>
{isHome && isLoading && query !== '' && <Spinner />} {isHome && isLoading && query !== '' && <Spinner />}

View file

@ -56,10 +56,7 @@ export const LibraryCommandItem = ({
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
style={{ height: '40px', width: '100%' }} style={{ height: '40px', width: '100%' }}
> >
<div <div className={styles.itemGrid} style={{ '--item-height': '40px' } as CSSProperties}>
className={styles.itemGrid}
style={{ '--item-height': '40px' } as CSSProperties}
>
<div className={styles.imageWrapper}> <div className={styles.imageWrapper}>
<Image <Image
alt="cover" alt="cover"
@ -71,21 +68,13 @@ export const LibraryCommandItem = ({
</div> </div>
<div className={styles.metadataWrapper}> <div className={styles.metadataWrapper}>
<Text overflow="hidden">{title}</Text> <Text overflow="hidden">{title}</Text>
<Text <Text isMuted overflow="hidden">
isMuted
overflow="hidden"
>
{subtitle} {subtitle}
</Text> </Text>
</div> </div>
</div> </div>
{isHovered && ( {isHovered && (
<Group <Group align="center" gap="sm" justify="flex-end" wrap="nowrap">
align="center"
gap="sm"
justify="flex-end"
wrap="nowrap"
>
<ActionIcon <ActionIcon
disabled={disabled} disabled={disabled}
icon="mediaPlay" icon="mediaPlay"

View file

@ -49,15 +49,9 @@ export const SearchHeader = ({ navigationId, tableRef }: SearchHeaderProps) => {
}, 200); }, 200);
return ( return (
<Stack <Stack gap={0} ref={cq.ref}>
gap={0}
ref={cq.ref}
>
<PageHeader> <PageHeader>
<Flex <Flex justify="space-between" w="100%">
justify="space-between"
w="100%"
>
<LibraryHeaderBar> <LibraryHeaderBar>
<LibraryHeaderBar.Title>Search</LibraryHeaderBar.Title> <LibraryHeaderBar.Title>Search</LibraryHeaderBar.Title>
</LibraryHeaderBar> </LibraryHeaderBar>

View file

@ -16,14 +16,8 @@ const SearchRoute = () => {
return ( return (
<AnimatedPage key={`search-${navigationId}`}> <AnimatedPage key={`search-${navigationId}`}>
<SearchHeader <SearchHeader navigationId={navigationId} tableRef={tableRef} />
navigationId={navigationId} <SearchContent key={`page-${itemType}`} tableRef={tableRef} />
tableRef={tableRef}
/>
<SearchContent
key={`page-${itemType}`}
tableRef={tableRef}
/>
</AnimatedPage> </AnimatedPage>
); );
}; };

View file

@ -31,15 +31,8 @@ interface AddServerFormProps {
function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) { function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) {
return ( return (
<Stack <Stack align="center" justify="center">
align="center" <img height="50" src={icon} width="50" />
justify="center"
>
<img
height="50"
src={icon}
width="50"
/>
<Text>{label}</Text> <Text>{label}</Text>
</Stack> </Stack>
); );
@ -47,30 +40,15 @@ function ServerIconWithLabel({ icon, label }: { icon: string; label: string }) {
const SERVER_TYPES = [ const SERVER_TYPES = [
{ {
label: ( label: <ServerIconWithLabel icon={JellyfinIcon} label="Jellyfin" />,
<ServerIconWithLabel
icon={JellyfinIcon}
label="Jellyfin"
/>
),
value: ServerType.JELLYFIN, value: ServerType.JELLYFIN,
}, },
{ {
label: ( label: <ServerIconWithLabel icon={NavidromeIcon} label="Navidrome" />,
<ServerIconWithLabel
icon={NavidromeIcon}
label="Navidrome"
/>
),
value: ServerType.NAVIDROME, value: ServerType.NAVIDROME,
}, },
{ {
label: ( label: <ServerIconWithLabel icon={SubsonicIcon} label="OpenSubsonic" />,
<ServerIconWithLabel
icon={SubsonicIcon}
label="OpenSubsonic"
/>
),
value: ServerType.SUBSONIC, value: ServerType.SUBSONIC,
}, },
]; ];
@ -174,10 +152,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Stack <Stack m={5} ref={focusTrapRef}>
m={5}
ref={focusTrapRef}
>
<SegmentedControl <SegmentedControl
data={SERVER_TYPES} data={SERVER_TYPES}
disabled={Boolean(serverLock)} disabled={Boolean(serverLock)}
@ -238,15 +213,9 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
{...form.getInputProps('legacyAuth', { type: 'checkbox' })} {...form.getInputProps('legacyAuth', { type: 'checkbox' })}
/> />
)} )}
<Group <Group grow justify="flex-end">
grow
justify="flex-end"
>
{onCancel && ( {onCancel && (
<Button <Button onClick={onCancel} variant="subtle">
onClick={onCancel}
variant="subtle"
>
{t('common.cancel', { postProcess: 'titleCase' })} {t('common.cancel', { postProcess: 'titleCase' })}
</Button> </Button>
)} )}

View file

@ -33,10 +33,7 @@ interface EditServerFormProps {
const ModifiedFieldIndicator = () => { const ModifiedFieldIndicator = () => {
return ( return (
<Tooltip label={i18n.t('common.modified', { postProcess: 'titleCase' }) as string}> <Tooltip label={i18n.t('common.modified', { postProcess: 'titleCase' }) as string}>
<Icon <Icon color="warn" icon="info" />
color="warn"
icon="info"
/>
</Tooltip> </Tooltip>
); );
}; };
@ -193,17 +190,10 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
/> />
)} )}
<Group justify="flex-end"> <Group justify="flex-end">
<Button <Button onClick={onCancel} variant="subtle">
onClick={onCancel}
variant="subtle"
>
{t('common.cancel', { postProcess: 'titleCase' })} {t('common.cancel', { postProcess: 'titleCase' })}
</Button> </Button>
<Button <Button loading={isLoading} type="submit" variant="filled">
loading={isLoading}
type="submit"
variant="filled"
>
{t('common.save', { postProcess: 'titleCase' })} {t('common.save', { postProcess: 'titleCase' })}
</Button> </Button>
</Group> </Group>

View file

@ -66,11 +66,7 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
/> />
) : ( ) : (
<Stack> <Stack>
<Table <Table layout="fixed" variant="vertical" withTableBorder>
layout="fixed"
variant="vertical"
withTableBorder
>
<Table.Tbody> <Table.Tbody>
<Table.Tr> <Table.Tr>
<Table.Th> <Table.Th>

View file

@ -73,10 +73,7 @@ export const ServerList = () => {
{Object.keys(serverListQuery)?.map((serverId) => { {Object.keys(serverListQuery)?.map((serverId) => {
const server = serverListQuery[serverId]; const server = serverListQuery[serverId];
return ( return (
<Accordion.Item <Accordion.Item key={server.id} value={server.name}>
key={server.id}
value={server.name}
>
<Accordion.Control> <Accordion.Control>
<Group> <Group>
<img <img
@ -103,10 +100,7 @@ export const ServerList = () => {
</Accordion.Item> </Accordion.Item>
); );
})} })}
<Group <Group grow pt="md">
grow
pt="md"
>
<Button <Button
autoFocus autoFocus
leftSection={<Icon icon="add" />} leftSection={<Icon icon="add" />}

View file

@ -22,11 +22,7 @@ export const ContextMenuSettings = () => {
<> <>
<SettingsOptions <SettingsOptions
control={ control={
<Button <Button onClick={() => setOpen(!open)} size="compact-md" variant="filled">
onClick={() => setOpen(!open)}
size="compact-md"
variant="filled"
>
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })} {t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
</Button> </Button>
} }

View file

@ -35,17 +35,8 @@ export const DraggableItem = ({ handleChangeDisabled, item, value }: DraggableIt
const dragControls = useDragControls(); const dragControls = useDragControls();
return ( return (
<Reorder.Item <Reorder.Item as="div" dragControls={dragControls} dragListener={false} value={item}>
as="div" <Group py="md" style={{ boxShadow: '0 1px 3px rgba(0,0,0,.1)' }} wrap="nowrap">
dragControls={dragControls}
dragListener={false}
value={item}
>
<Group
py="md"
style={{ boxShadow: '0 1px 3px rgba(0,0,0,.1)' }}
wrap="nowrap"
>
<Checkbox <Checkbox
checked={!item.disabled} checked={!item.disabled}
onChange={(e) => handleChangeDisabled(item.id, e.target.checked)} onChange={(e) => handleChangeDisabled(item.id, e.target.checked)}

Some files were not shown because too many files have changed in this diff Show more