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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
473 changed files with 12469 additions and 11607 deletions

View file

@ -1,5 +1,4 @@
import { RowNode } from '@ag-grid-community/core';
import { Divider, Group, Portal, Stack } from '@mantine/core';
import {
useClickOutside,
useMergedRef,
@ -8,42 +7,14 @@ import {
useViewportSize,
} from '@mantine/hooks';
import { closeAllModals, openContextModal, openModal } from '@mantine/modals';
import { AnimatePresence } from 'framer-motion';
import isElectron from 'is-electron';
import { AnimatePresence } from 'motion/react';
import { createContext, Fragment, ReactNode, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
RiAddBoxFill,
RiAddCircleFill,
RiArrowDownLine,
RiArrowGoForwardLine,
RiArrowRightSFill,
RiArrowUpLine,
RiCloseCircleLine,
RiDeleteBinFill,
RiDislikeFill,
RiDownload2Line,
RiHeartFill,
RiInformationFill,
RiPlayFill,
RiPlayListAddFill,
RiRadio2Fill,
RiShareForwardFill,
RiShuffleFill,
RiStarFill,
} from 'react-icons/ri';
import { api } from '/@/renderer/api';
import { controller } from '/@/renderer/api/controller';
import {
ConfirmModal,
ContextMenu,
ContextMenuButton,
HoverCard,
Rating,
Text,
toast,
} from '/@/renderer/components';
import { ContextMenu, ContextMenuButton } from '/@/renderer/components/context-menu/context-menu';
import {
ContextMenuItemType,
OpenContextMenuProps,
@ -66,6 +37,16 @@ import {
import { usePlaybackType } from '/@/renderer/store/settings.store';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
import { hasFeature } from '/@/shared/api/utils';
import { Divider } from '/@/shared/components/divider/divider';
import { Group } from '/@/shared/components/group/group';
import { HoverCard } from '/@/shared/components/hover-card/hover-card';
import { Icon } from '/@/shared/components/icon/icon';
import { ConfirmModal } from '/@/shared/components/modal/modal';
import { Portal } from '/@/shared/components/portal/portal';
import { Rating } from '/@/shared/components/rating/rating';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast';
import {
AnyLibraryItem,
AnyLibraryItems,
@ -113,6 +94,7 @@ function RatingIcon({ rating }: { rating: number }) {
readOnly
style={{
pointerEvents: 'none',
size: 'var(--theme-font-size-md)',
}}
value={rating}
/>
@ -292,7 +274,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
{ctx.data.map((item) => (
<li key={item.id}>
<Group>
<Text $secondary>{item.name}</Text>
<Text isMuted>{item.name}</Text>
</Group>
</li>
))}
@ -750,13 +732,13 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
addToFavorites: {
id: 'addToFavorites',
label: t('page.contextMenu.addToFavorites', { postProcess: 'sentenceCase' }),
leftIcon: <RiHeartFill size="1.1rem" />,
leftIcon: <Icon icon="favorite" />,
onClick: handleAddToFavorites,
},
addToPlaylist: {
id: 'addToPlaylist',
label: t('page.contextMenu.addToPlaylist', { postProcess: 'sentenceCase' }),
leftIcon: <RiPlayListAddFill size="1.1rem" />,
leftIcon: <Icon icon="playlistAdd" />,
onClick: handleAddToPlaylist,
},
createPlaylist: {
@ -767,86 +749,86 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
deletePlaylist: {
id: 'deletePlaylist',
label: t('page.contextMenu.deletePlaylist', { postProcess: 'sentenceCase' }),
leftIcon: <RiDeleteBinFill size="1.1rem" />,
leftIcon: <Icon icon="playlistDelete" />,
onClick: openDeletePlaylistModal,
},
deselectAll: {
id: 'deselectAll',
label: t('page.contextMenu.deselectAll', { postProcess: 'sentenceCase' }),
leftIcon: <RiCloseCircleLine size="1.1rem" />,
leftIcon: <Icon icon="remove" />,
onClick: handleDeselectAll,
},
download: {
disabled: ctx.data?.length !== 1,
id: 'download',
label: t('page.contextMenu.download', { postProcess: 'sentenceCase' }),
leftIcon: <RiDownload2Line size="1.1rem" />,
leftIcon: <Icon icon="download" />,
onClick: handleDownload,
},
moveToBottomOfQueue: {
id: 'moveToBottomOfQueue',
label: t('page.contextMenu.moveToBottom', { postProcess: 'sentenceCase' }),
leftIcon: <RiArrowDownLine size="1.1rem" />,
leftIcon: <Icon icon="arrowDownToLine" />,
onClick: handleMoveToBottom,
},
moveToNextOfQueue: {
id: 'moveToNext',
label: t('page.contextMenu.moveToNext', { postProcess: 'sentenceCase' }),
leftIcon: <RiArrowGoForwardLine size="1.1rem" />,
leftIcon: <Icon icon="mediaPlayNext" />,
onClick: handleMoveToNext,
},
moveToTopOfQueue: {
id: 'moveToTopOfQueue',
label: t('page.contextMenu.moveToTop', { postProcess: 'sentenceCase' }),
leftIcon: <RiArrowUpLine size="1.1rem" />,
leftIcon: <Icon icon="arrowUpToLine" />,
onClick: handleMoveToTop,
},
play: {
id: 'play',
label: t('page.contextMenu.play', { postProcess: 'sentenceCase' }),
leftIcon: <RiPlayFill size="1.1rem" />,
leftIcon: <Icon icon="mediaPlay" />,
onClick: () => handlePlay(Play.NOW),
},
playLast: {
id: 'playLast',
label: t('page.contextMenu.addLast', { postProcess: 'sentenceCase' }),
leftIcon: <RiAddBoxFill size="1.1rem" />,
leftIcon: <Icon icon="mediaPlayLast" />,
onClick: () => handlePlay(Play.LAST),
},
playNext: {
id: 'playNext',
label: t('page.contextMenu.addNext', { postProcess: 'sentenceCase' }),
leftIcon: <RiAddCircleFill size="1.1rem" />,
leftIcon: <Icon icon="mediaPlayNext" />,
onClick: () => handlePlay(Play.NEXT),
},
playShuffled: {
id: 'playShuffled',
label: t('page.contextMenu.playShuffled', { postProcess: 'sentenceCase' }),
leftIcon: <RiShuffleFill size="1.1rem" />,
leftIcon: <Icon icon="mediaShuffle" />,
onClick: () => handlePlay(Play.SHUFFLE),
},
playSimilarSongs: {
id: 'playSimilarSongs',
label: t('page.contextMenu.playSimilarSongs', { postProcess: 'sentenceCase' }),
leftIcon: <RiRadio2Fill size="1.1rem" />,
leftIcon: <Icon icon="radio" />,
onClick: handleSimilar,
},
removeFromFavorites: {
id: 'removeFromFavorites',
label: t('page.contextMenu.removeFromFavorites', { postProcess: 'sentenceCase' }),
leftIcon: <RiDislikeFill size="1.1rem" />,
leftIcon: <Icon icon="unfavorite" />,
onClick: handleRemoveFromFavorites,
},
removeFromPlaylist: {
id: 'removeFromPlaylist',
label: t('page.contextMenu.removeFromPlaylist', { postProcess: 'sentenceCase' }),
leftIcon: <RiDeleteBinFill size="1.1rem" />,
leftIcon: <Icon icon="playlistDelete" />,
onClick: handleRemoveFromPlaylist,
},
removeFromQueue: {
id: 'removeSongs',
label: t('page.contextMenu.removeFromQueue', { postProcess: 'sentenceCase' }),
leftIcon: <RiDeleteBinFill size="1.1rem" />,
leftIcon: <Icon icon="delete" />,
onClick: handleRemoveSelected,
},
setRating: {
@ -884,22 +866,22 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
],
id: 'setRating',
label: t('action.setRating', { postProcess: 'sentenceCase' }),
leftIcon: <RiStarFill size="1.1rem" />,
leftIcon: <Icon icon="star" />,
onClick: () => {},
rightIcon: <RiArrowRightSFill size="1.2rem" />,
rightIcon: <Icon icon="arrowRightS" />,
},
shareItem: {
disabled: !hasFeature(server, ServerFeature.SHARING_ALBUM_SONG),
id: 'shareItem',
label: t('page.contextMenu.shareItem', { postProcess: 'sentenceCase' }),
leftIcon: <RiShareForwardFill size="1.1rem" />,
leftIcon: <Icon icon="share" />,
onClick: handleShareItem,
},
showDetails: {
disabled: ctx.data?.length !== 1 || !ctx.data[0].itemType,
id: 'showDetails',
label: t('page.contextMenu.showDetails', { postProcess: 'sentenceCase' }),
leftIcon: <RiInformationFill />,
leftIcon: <Icon icon="info" />,
onClick: handleOpenItemDetails,
},
};
@ -946,10 +928,10 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
xPos={ctx.xPos}
yPos={ctx.yPos}
>
<Stack spacing={0}>
<Stack gap={0}>
<Stack
gap={0}
onClick={closeContextMenu}
spacing={0}
>
{ctx.menuItems?.map((item) => {
return (
@ -957,7 +939,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
<Fragment key={`context-menu-${item.id}`}>
{item.children ? (
<HoverCard
offset={5}
offset={0}
position="right"
>
<HoverCard.Target>
@ -982,7 +964,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
</ContextMenuButton>
</HoverCard.Target>
<HoverCard.Dropdown>
<Stack spacing={0}>
<Stack gap={0}>
{contextMenuItems[
item.id
].children?.map((child) => (
@ -1020,9 +1002,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
{item.divider && (
<Divider
color="rgb(62, 62, 62)"
key={`context-menu-divider-${item.id}`}
size="sm"
/>
)}
</Fragment>
@ -1030,10 +1010,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
);
})}
</Stack>
<Divider
color="rgb(62, 62, 62)"
size="sm"
/>
<ContextMenuButton disabled>
{t('page.contextMenu.numberSelected', {
count: ctx.data?.length || 0,