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
htmlWhitespaceSensitivity: strict
endOfLine: lf
singleAttributePerLine: true
singleAttributePerLine: false
bracketSpacing: true
plugins:
- prettier-plugin-packagejson

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,36 +16,11 @@ type Options = {
type Presets = 'actions' | 'duration' | 'rowIndex' | 'userFavorite' | 'userRating';
const headerPresets = {
actions: (
<Icon
icon="ellipsisHorizontal"
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"
/>
),
actions: <Icon icon="ellipsisHorizontal" 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 = (

View file

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

View file

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

View file

@ -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>

View file

@ -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>

View file

@ -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')}

View file

@ -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>

View file

@ -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>

View file

@ -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" />}

View file

@ -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>

View file

@ -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

View file

@ -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>
);

View file

@ -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]}

View file

@ -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>

View file

@ -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" />

View file

@ -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}

View file

@ -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" />

View file

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

View file

@ -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>
);

View file

@ -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>

View file

@ -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',
})}

View file

@ -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>
);

View file

@ -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]}

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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}

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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={

View file

@ -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>
);

View file

@ -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]}

View file

@ -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>

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>

View file

@ -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,

View file

@ -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 ? (

View file

@ -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}

View file

@ -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>
);

View file

@ -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"

View file

@ -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>
);
};

View file

@ -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>
);

View file

@ -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>

View file

@ -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>
)}

View file

@ -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"

View file

@ -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`}

View file

@ -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;
};

View file

@ -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}

View file

@ -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"

View file

@ -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} />;
};

View file

@ -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

View file

@ -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}

View file

@ -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',
})}

View file

@ -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>

View file

@ -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

View file

@ -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>
);

View file

@ -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' })}

View file

@ -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

View file

@ -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

View file

@ -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'}

View file

@ -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>
);

View file

@ -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 />}

View file

@ -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"

View file

@ -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>

View file

@ -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>
);
};

View file

@ -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>
)}

View file

@ -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>

View file

@ -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>

View file

@ -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" />}

View file

@ -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>
}

View file

@ -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)}

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