mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
Migrate to Mantine v8 and Design Changes (#961)
* mantine v8 migration * various design changes and improvements
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import { Center, Stack } from '@mantine/core';
|
||||
import { RiAlbumFill, RiPlayListFill, RiUserVoiceFill } from 'react-icons/ri';
|
||||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import { generatePath, useNavigate } from 'react-router-dom';
|
||||
import { SimpleImg } from 'react-simple-img';
|
||||
import { ListChildComponentProps } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CardRows } from '/@/renderer/components/card';
|
||||
import { Skeleton } from '/@/renderer/components/skeleton';
|
||||
import styles from './poster-card.module.css';
|
||||
|
||||
import { CardRows } from '/@/renderer/components/card/card-rows';
|
||||
import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/grid-card-controls';
|
||||
import { Image } from '/@/shared/components/image/image';
|
||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
@@ -39,93 +41,6 @@ interface BaseGridCardProps {
|
||||
listChildProps: Omit<ListChildComponentProps, 'data' | 'style'>;
|
||||
}
|
||||
|
||||
const PosterCardContainer = styled.div<{ $isHidden?: boolean; $itemGap: number }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: ${({ $itemGap }) => $itemGap}px;
|
||||
overflow: hidden;
|
||||
pointer-events: auto;
|
||||
opacity: ${({ $isHidden }) => ($isHidden ? 0 : 1)};
|
||||
|
||||
.card-controls {
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const LinkContainer = styled.div`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const ImageContainer = styled.div<{ $isFavorite?: boolean }>`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
aspect-ratio: 1/1;
|
||||
overflow: hidden;
|
||||
background: var(--card-default-bg);
|
||||
border-radius: var(--card-poster-radius);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
user-select: none;
|
||||
background: linear-gradient(0deg, rgb(0 0 0 / 100%) 35%, rgb(0 0 0 / 0%) 100%);
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.$isFavorite &&
|
||||
`
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: -50px;
|
||||
left: -50px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-color: var(--primary-color);
|
||||
box-shadow: 0 0 10px 8px rgba(0, 0, 0, 80%);
|
||||
transform: rotate(-45deg);
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
}
|
||||
`}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .card-controls {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const Image = styled(SimpleImg)`
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100% !important;
|
||||
max-height: 100%;
|
||||
border: 0;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
object-fit: var(--image-fit);
|
||||
}
|
||||
`;
|
||||
|
||||
const DetailContainer = styled.div`
|
||||
margin-top: 0.5rem;
|
||||
`;
|
||||
|
||||
export const PosterCard = ({
|
||||
columnIndex,
|
||||
controls,
|
||||
@@ -135,6 +50,8 @@ export const PosterCard = ({
|
||||
}: BaseGridCardProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
if (data) {
|
||||
const path = generatePath(
|
||||
controls.route.route as string,
|
||||
@@ -146,97 +63,68 @@ export const PosterCard = ({
|
||||
}, {}),
|
||||
);
|
||||
|
||||
let Placeholder = RiAlbumFill;
|
||||
|
||||
switch (controls.itemType) {
|
||||
case LibraryItem.ALBUM:
|
||||
Placeholder = RiAlbumFill;
|
||||
break;
|
||||
case LibraryItem.ALBUM_ARTIST:
|
||||
Placeholder = RiUserVoiceFill;
|
||||
break;
|
||||
case LibraryItem.ARTIST:
|
||||
Placeholder = RiUserVoiceFill;
|
||||
break;
|
||||
case LibraryItem.PLAYLIST:
|
||||
Placeholder = RiPlayListFill;
|
||||
break;
|
||||
default:
|
||||
Placeholder = RiAlbumFill;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<PosterCardContainer
|
||||
$itemGap={controls.itemGap}
|
||||
<div
|
||||
className={styles.container}
|
||||
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
margin: controls.itemGap,
|
||||
}}
|
||||
>
|
||||
<LinkContainer onClick={() => navigate(path)}>
|
||||
<ImageContainer $isFavorite={data?.userFavorite}>
|
||||
{data?.imageUrl ? (
|
||||
<Image
|
||||
importance="auto"
|
||||
placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
|
||||
src={data?.imageUrl}
|
||||
/>
|
||||
) : (
|
||||
<Center
|
||||
sx={{
|
||||
background: 'var(--placeholder-bg)',
|
||||
borderRadius: 'var(--card-default-radius)',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Placeholder
|
||||
color="var(--placeholder-fg)"
|
||||
size={35}
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
<div
|
||||
className={styles.linkContainer}
|
||||
onClick={() => navigate(path)}
|
||||
>
|
||||
<div
|
||||
className={`${styles.imageContainer} ${data?.userFavorite ? styles.isFavorite : ''}`}
|
||||
>
|
||||
<Image
|
||||
className={styles.image}
|
||||
src={data?.imageUrl}
|
||||
/>
|
||||
<GridCardControls
|
||||
handleFavorite={controls.handleFavorite}
|
||||
handlePlayQueueAdd={controls.handlePlayQueueAdd}
|
||||
isHovered={isHovered}
|
||||
itemData={data}
|
||||
itemType={controls.itemType}
|
||||
resetInfiniteLoaderCache={controls.resetInfiniteLoaderCache}
|
||||
/>
|
||||
</ImageContainer>
|
||||
</LinkContainer>
|
||||
<DetailContainer>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.detailContainer}>
|
||||
<CardRows
|
||||
data={data}
|
||||
rows={controls.cardRows}
|
||||
/>
|
||||
</DetailContainer>
|
||||
</PosterCardContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PosterCardContainer
|
||||
$isHidden={isHidden}
|
||||
$itemGap={controls.itemGap}
|
||||
<div
|
||||
className={clsx(styles.container, isHidden && styles.hidden)}
|
||||
key={`card-${columnIndex}-${listChildProps.index}`}
|
||||
style={{
|
||||
margin: controls.itemGap,
|
||||
}}
|
||||
>
|
||||
<Skeleton
|
||||
radius="sm"
|
||||
visible
|
||||
>
|
||||
<ImageContainer />
|
||||
</Skeleton>
|
||||
<DetailContainer>
|
||||
<Stack spacing="sm">
|
||||
<div className={styles.imageContainer}>
|
||||
<Skeleton className={styles.image} />
|
||||
</div>
|
||||
<div className={styles.detailContainer}>
|
||||
<Stack gap="xs">
|
||||
{(controls?.cardRows || []).map((row, index) => (
|
||||
<Skeleton
|
||||
height={14}
|
||||
className={styles.row}
|
||||
key={`${index}-${columnIndex}-${row.arrayProperty}`}
|
||||
radius="sm"
|
||||
visible
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</DetailContainer>
|
||||
</PosterCardContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user