Migrate to Mantine v8 and Design Changes (#961)

* mantine v8 migration

* various design changes and improvements
This commit is contained in:
Jeff
2025-06-24 00:04:36 -07:00
committed by GitHub
parent bea55d48a8
commit c1330d92b2
473 changed files with 12469 additions and 11607 deletions
@@ -1,20 +1,16 @@
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
import { Box, Group, Stack } from '@mantine/core';
import { useSetState } from '@mantine/hooks';
import { MutableRefObject, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaLastfmSquare } from 'react-icons/fa';
import { RiHeartFill, RiHeartLine, RiMoreFill, RiSettings2Fill } from 'react-icons/ri';
import { SiMusicbrainz } from 'react-icons/si';
import { generatePath, useParams } from 'react-router';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import styles from './album-detail-content.module.css';
import { queryKeys } from '/@/renderer/api/query-keys';
import { Button, Popover, Spoiler } from '/@/renderer/components';
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel';
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel/grid-carousel';
import {
getColumnDefs,
TableConfigDropdown,
@@ -47,6 +43,12 @@ import {
useTableSettings,
} from '/@/renderer/store/settings.store';
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { Popover } from '/@/shared/components/popover/popover';
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
import { Stack } from '/@/shared/components/stack/stack';
import {
AlbumListQuery,
AlbumListSort,
@@ -60,19 +62,6 @@ const isFullWidthRow = (node: RowNode) => {
return node.id?.startsWith('disc-');
};
const ContentContainer = styled.div`
position: relative;
z-index: 0;
`;
const DetailContainer = styled.div`
display: flex;
flex-direction: column;
gap: 2rem;
padding: 1rem 2rem 5rem;
overflow: hidden;
`;
interface AlbumDetailContentProps {
background?: string;
tableRef: MutableRefObject<AgGridReactType | null>;
@@ -330,72 +319,73 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
const mbzId = detailQuery?.data?.mbzId;
return (
<ContentContainer>
<LibraryBackgroundOverlay $backgroundColor={background} />
<DetailContainer>
<Box component="section">
<div
className={styles.contentContainer}
ref={cq.ref}
>
<LibraryBackgroundOverlay backgroundColor={background} />
<div className={styles.detailContainer}>
<section>
<Group
position="apart"
spacing="sm"
gap="sm"
justify="space-between"
>
<Group>
<PlayButton onClick={() => handlePlay(playButtonBehavior)} />
<Button
compact
loading={
createFavoriteMutation.isLoading ||
deleteFavoriteMutation.isLoading
}
onClick={handleFavorite}
variant="subtle"
>
{detailQuery?.data?.userFavorite ? (
<RiHeartFill
color="red"
size={20}
/>
) : (
<RiHeartLine size={20} />
)}
</Button>
<Button
compact
onClick={(e) => {
if (!detailQuery?.data) return;
handleGeneralContextMenu(e, [detailQuery.data!]);
}}
variant="subtle"
>
<RiMoreFill size={20} />
</Button>
<Group gap="xs">
<ActionIcon
icon="favorite"
iconProps={{
fill: detailQuery?.data?.userFavorite
? 'primary'
: undefined,
}}
loading={
createFavoriteMutation.isLoading ||
deleteFavoriteMutation.isLoading
}
onClick={handleFavorite}
size="lg"
variant="transparent"
/>
<ActionIcon
icon="ellipsisHorizontal"
onClick={(e) => {
if (!detailQuery?.data) return;
handleGeneralContextMenu(e, [detailQuery.data!]);
}}
size="lg"
variant="transparent"
/>
</Group>
</Group>
<Popover position="bottom-end">
<Popover.Target>
<Button
compact
size="md"
variant="subtle"
>
<RiSettings2Fill size={20} />
</Button>
<ActionIcon
icon="settings"
onClick={(e) => {
if (!detailQuery?.data) return;
handleGeneralContextMenu(e, [detailQuery.data!]);
}}
size="lg"
variant="transparent"
/>
</Popover.Target>
<Popover.Dropdown>
<TableConfigDropdown type="albumDetail" />
</Popover.Dropdown>
</Popover>
</Group>
</Box>
</section>
{showGenres && (
<Box component="section">
<Group spacing="sm">
<section>
<Group gap="sm">
{detailQuery?.data?.genres?.map((genre) => (
<Button
compact
component={Link}
key={`genre-${genre.id}`}
radius={0}
size="md"
radius="md"
size="compact-md"
to={generatePath(genreRoute, {
genreId: genre.id,
})}
@@ -405,35 +395,38 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
</Button>
))}
</Group>
</Box>
</section>
)}
{externalLinks && (lastFM || musicBrainz) ? (
<Box component="section">
<Group spacing="sm">
{lastFM && (
<Button
compact
component="a"
href={`https://www.last.fm/music/${encodeURIComponent(
detailQuery?.data?.albumArtist || '',
)}/${encodeURIComponent(detailQuery.data?.name || '')}`}
radius="md"
rel="noopener noreferrer"
size="md"
target="_blank"
tooltip={{
label: t('action.openIn.lastfm'),
}}
variant="subtle"
>
<FaLastfmSquare size={25} />
</Button>
)}
{musicBrainz && mbzId ? (
<Button
compact
<section>
<Group gap="sm">
<ActionIcon
component="a"
href={`https://www.last.fm/music/${encodeURIComponent(
detailQuery?.data?.albumArtist || '',
)}/${encodeURIComponent(detailQuery.data?.name || '')}`}
icon="brandLastfm"
iconProps={{
fill: 'default',
size: 'xl',
}}
radius="md"
rel="noopener noreferrer"
target="_blank"
tooltip={{
label: t('action.openIn.lastfm'),
}}
variant="subtle"
/>
{mbzId ? (
<ActionIcon
component="a"
href={`https://musicbrainz.org/release/${mbzId}`}
icon="brandMusicBrainz"
iconProps={{
fill: 'default',
size: 'xl',
}}
radius="md"
rel="noopener noreferrer"
size="md"
@@ -442,19 +435,17 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
label: t('action.openIn.musicbrainz'),
}}
variant="subtle"
>
<SiMusicbrainz size={25} />
</Button>
/>
) : null}
</Group>
</Box>
</section>
) : null}
{comment && (
<Box component="section">
<section>
<Spoiler maxHeight={75}>{replaceURLWithHTMLLinks(comment)}</Spoiler>
</Box>
</section>
)}
<Box style={{ minHeight: '300px' }}>
<div style={{ minHeight: '300px' }}>
<VirtualTable
autoFitColumns={tableConfig.autoFit}
autoHeight
@@ -491,11 +482,11 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
suppressLoadingOverlay
suppressRowDrag
/>
</Box>
</div>
<Stack
gap="lg"
mt="3rem"
ref={cq.ref}
spacing="lg"
>
{cq.height || cq.width ? (
<>
@@ -547,7 +538,7 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
</>
) : null}
</Stack>
</DetailContainer>
</ContentContainer>
</div>
</div>
);
};