mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
adjust feature carousel design
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
width: 100%;
|
||||
min-height: 440px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--theme-radius-md);
|
||||
border-radius: var(--theme-radius-lg);
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
@@ -56,16 +56,17 @@
|
||||
.title-section {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
min-height: 60px;
|
||||
max-height: 60px;
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
@@ -76,6 +77,22 @@
|
||||
max-height: 250px;
|
||||
}
|
||||
|
||||
.play-button-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 20;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.image-section:hover .play-button-overlay {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.metadata-section {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
@@ -102,17 +119,44 @@
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.album-image {
|
||||
.album-image-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 180px;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
border-radius: var(--theme-radius-lg);
|
||||
box-shadow: 0 8px 24px rgb(0 0 0 / 60%);
|
||||
transition: box-shadow 0.3s ease;
|
||||
filter: drop-shadow(0 10px 30px rgb(0 0 0 / 50%)) drop-shadow(0 4px 12px rgb(0 0 0 / 40%));
|
||||
transition: filter 0.3s ease;
|
||||
}
|
||||
|
||||
.image-link:hover .album-image {
|
||||
box-shadow: 0 12px 32px rgb(0 0 0 / 40%);
|
||||
.album-image-container::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
background-color: rgb(0 0 0 / 0%);
|
||||
border-radius: var(--theme-radius-lg);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.image-section:hover .album-image-container::before {
|
||||
background-color: rgb(0 0 0 / 40%);
|
||||
}
|
||||
|
||||
.album-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
border-radius: var(--theme-radius-lg);
|
||||
}
|
||||
|
||||
.carousel-item:hover .album-image-container,
|
||||
.carousel-link:hover .album-image-container {
|
||||
filter: drop-shadow(0 16px 40px rgb(0 0 0 / 60%)) drop-shadow(0 6px 16px rgb(0 0 0 / 50%));
|
||||
}
|
||||
|
||||
.artist-link {
|
||||
@@ -195,7 +239,7 @@
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
.album-image {
|
||||
.album-image-container {
|
||||
max-width: 160px;
|
||||
}
|
||||
}
|
||||
@@ -231,7 +275,7 @@
|
||||
max-height: 110px;
|
||||
}
|
||||
|
||||
.album-image {
|
||||
.album-image-container {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
@@ -263,7 +307,7 @@
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
.album-image {
|
||||
.album-image-container {
|
||||
max-width: 220px;
|
||||
}
|
||||
}
|
||||
@@ -295,7 +339,7 @@
|
||||
max-height: 130px;
|
||||
}
|
||||
|
||||
.album-image {
|
||||
.album-image-container {
|
||||
max-width: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,21 @@ import { generatePath, Link } from 'react-router';
|
||||
|
||||
import styles from './feature-carousel.module.css';
|
||||
|
||||
import { ItemCard } from '/@/renderer/components/item-card/item-card';
|
||||
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import { BackgroundOverlay } from '/@/renderer/features/shared/components/library-background-overlay';
|
||||
import { PlayButtonGroup } from '/@/renderer/features/shared/components/play-button-group';
|
||||
import { useContainerQuery, useFastAverageColor } from '/@/renderer/hooks';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Badge } from '/@/shared/components/badge/badge';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Image } from '/@/shared/components/image/image';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { Album, LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
|
||||
const containerVariants = {
|
||||
animate: (custom: { isNext: boolean }) => ({
|
||||
@@ -98,11 +101,17 @@ const CarouselItem = ({ album }: CarouselItemProps) => {
|
||||
srcLoaded: true,
|
||||
});
|
||||
|
||||
const controls = useDefaultItemListControls();
|
||||
const server = useCurrentServer();
|
||||
const { addToQueueByFetch } = usePlayer();
|
||||
|
||||
const handlePlay = (type: Play) => {
|
||||
if (!server?.id) return;
|
||||
addToQueueByFetch(server.id, [album.id], LibraryItem.ALBUM, type);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.carouselItem}>
|
||||
<BackgroundOverlay backgroundColor={backgroundColor} opacity={1} />
|
||||
<BackgroundOverlay backgroundColor={backgroundColor} opacity={0.7} />
|
||||
<Link
|
||||
className={styles.carouselLink}
|
||||
state={{ item: album }}
|
||||
@@ -112,21 +121,20 @@ const CarouselItem = ({ album }: CarouselItemProps) => {
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.titleSection}>
|
||||
<TextTitle className={styles.title} fw={700} lineClamp={2} order={3}>
|
||||
<TextTitle className={styles.title} fw={800} lineClamp={2} order={3}>
|
||||
{album.name}
|
||||
</TextTitle>
|
||||
</div>
|
||||
|
||||
<div className={styles.imageSection}>
|
||||
<ItemCard
|
||||
controls={controls}
|
||||
data={album}
|
||||
enableNavigation={false}
|
||||
itemType={LibraryItem.ALBUM}
|
||||
rows={[]}
|
||||
type="poster"
|
||||
withControls
|
||||
<Image
|
||||
className={styles.albumImage}
|
||||
containerClassName={styles.albumImageContainer}
|
||||
src={album.imageUrl || undefined}
|
||||
/>
|
||||
<div className={styles.playButtonOverlay}>
|
||||
<PlayButtonGroup onPlay={handlePlay} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.metadataSection}>
|
||||
@@ -136,18 +144,26 @@ const CarouselItem = ({ album }: CarouselItemProps) => {
|
||||
className={styles.artist}
|
||||
fw={600}
|
||||
key={`artist-${artist.id}`}
|
||||
size="md"
|
||||
size="xl"
|
||||
>
|
||||
{artist.name}
|
||||
</Text>
|
||||
))}
|
||||
<Group gap="sm" justify="center" wrap="wrap">
|
||||
{album.genres?.slice(0, 2).map((genre) => (
|
||||
<Badge key={`genre-${genre.id}`} size="sm">
|
||||
<Badge
|
||||
key={`genre-${genre.id}`}
|
||||
size="lg"
|
||||
variant="transparent"
|
||||
>
|
||||
{genre.name}
|
||||
</Badge>
|
||||
))}
|
||||
{album.releaseYear && <Badge size="sm">{album.releaseYear}</Badge>}
|
||||
{album.releaseYear && (
|
||||
<Badge size="lg" variant="transparent">
|
||||
{album.releaseYear}
|
||||
</Badge>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.play-button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--theme-spacing-sm);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import styles from './play-button-group.module.css';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { PlayButton } from '/@/renderer/features/shared/components/play-button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { AppIconSelection } from '/@/shared/components/icon/icon';
|
||||
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
@@ -39,7 +40,7 @@ interface PlayButtonGroupProps {
|
||||
|
||||
export const PlayButtonGroup = ({ loading, onPlay }: PlayButtonGroupProps) => {
|
||||
return (
|
||||
<Group align="center" gap="md" justify="center">
|
||||
<div className={styles.playButtonGroup}>
|
||||
{playButtons.map((button) => (
|
||||
<Tooltip key={button.type} label={button.label} openDelay={2000}>
|
||||
<PlayButton
|
||||
@@ -52,6 +53,6 @@ export const PlayButtonGroup = ({ loading, onPlay }: PlayButtonGroupProps) => {
|
||||
/>
|
||||
</Tooltip>
|
||||
))}
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user