feishin/src/renderer/features/shared/components/library-header.tsx

151 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-05-14 08:25:02 -07:00
import { forwardRef, ReactNode, Ref, useCallback, useState } from 'react';
import { Center, Group } from '@mantine/core';
import { closeAllModals, openModal } from '@mantine/modals';
2024-08-25 17:50:46 -07:00
import { AutoTextSize } from 'auto-text-size';
import clsx from 'clsx';
import { useTranslation } from 'react-i18next';
2023-01-02 03:47:05 -08:00
import { Link } from 'react-router-dom';
2023-07-24 14:38:56 -07:00
import styles from './library-header.module.scss';
2023-01-05 21:59:07 -08:00
import { LibraryItem } from '/@/renderer/api/types';
2023-07-24 14:38:56 -07:00
import { Text } from '/@/renderer/components';
import { ItemImagePlaceholder } from '/@/renderer/features/shared/components/item-image-placeholder';
import { useGeneralSettings } from '/@/renderer/store';
2023-01-02 03:47:05 -08:00
interface LibraryHeaderProps {
2023-07-01 19:10:05 -07:00
background: string;
blur?: number;
2023-07-01 19:10:05 -07:00
children?: ReactNode;
imagePlaceholderUrl?: string | null;
imageUrl?: string | null;
item: { route: string; type: LibraryItem };
title: string;
2023-01-02 03:47:05 -08:00
}
export const LibraryHeader = forwardRef(
2023-07-01 19:10:05 -07:00
(
{
imageUrl,
imagePlaceholderUrl,
background,
blur,
title,
item,
children,
}: LibraryHeaderProps,
2023-07-01 19:10:05 -07:00
ref: Ref<HTMLDivElement>,
) => {
const { t } = useTranslation();
const [isImageError, setIsImageError] = useState<boolean | null>(false);
const { albumBackground } = useGeneralSettings();
const onImageError = () => {
setIsImageError(true);
};
const itemTypeString = () => {
switch (item.type) {
case LibraryItem.ALBUM:
return t('entity.album', { count: 1 });
case LibraryItem.ARTIST:
return t('entity.artist', { count: 1 });
case LibraryItem.ALBUM_ARTIST:
return t('entity.albumArtist', { count: 1 });
case LibraryItem.PLAYLIST:
return t('entity.playlist', { count: 1 });
case LibraryItem.SONG:
return t('entity.track', { count: 1 });
default:
return t('common.unknown');
}
};
2025-05-14 08:25:02 -07:00
const openImage = useCallback(() => {
if (imageUrl && !isImageError) {
const fullSized = imageUrl.replace(/&?(size|width|height)=\d+/, '');
openModal({
children: (
<Center
style={{
cursor: 'pointer',
height: 'calc(100vh - 80px)',
width: '100%',
}}
onClick={() => closeAllModals()}
>
<img
alt="cover"
src={fullSized}
style={{
maxHeight: '100%',
maxWidth: '100%',
}}
/>
</Center>
),
fullScreen: true,
});
}
}, [imageUrl, isImageError]);
2023-07-01 19:10:05 -07:00
return (
2023-07-24 14:38:56 -07:00
<div
ref={ref}
className={styles.libraryHeader}
2023-01-02 03:47:05 -08:00
>
2023-07-24 14:38:56 -07:00
<div
className={styles.background}
style={{ background, filter: `blur(${blur ?? 0}rem)` }}
/>
<div
className={clsx(styles.backgroundOverlay, {
[styles.opaqueOverlay]: albumBackground,
})}
2023-07-24 14:38:56 -07:00
/>
2025-05-14 08:25:02 -07:00
<div
className={styles.imageSection}
role="button"
style={{ cursor: 'pointer' }}
tabIndex={0}
onClick={() => openImage()}
onKeyDown={(event) =>
['Spacebar', ' ', 'Enter'].includes(event.key) && openImage()
}
>
{imageUrl && !isImageError ? (
2023-08-06 12:02:13 -07:00
<img
2023-07-01 19:10:05 -07:00
alt="cover"
2023-07-24 14:38:56 -07:00
className={styles.image}
2023-07-01 19:10:05 -07:00
placeholder={imagePlaceholderUrl || 'var(--placeholder-bg)'}
src={imageUrl}
2023-07-24 14:38:56 -07:00
style={{ height: '' }}
onError={onImageError}
2023-07-01 19:10:05 -07:00
/>
) : (
2023-07-24 14:38:56 -07:00
<ItemImagePlaceholder itemType={item.type} />
2023-07-01 19:10:05 -07:00
)}
2023-07-24 14:38:56 -07:00
</div>
<div className={styles.metadataSection}>
2023-07-01 19:10:05 -07:00
<Group>
<h2>
<Text
$link
component={Link}
to={item.route}
tt="uppercase"
weight={600}
>
{itemTypeString()}
</Text>
</h2>
2023-07-01 19:10:05 -07:00
</Group>
2024-08-25 17:50:46 -07:00
<h1 className={styles.title}>
<AutoTextSize mode="box">{title}</AutoTextSize>
</h1>
2023-07-01 19:10:05 -07:00
{children}
2023-07-24 14:38:56 -07:00
</div>
</div>
2023-07-01 19:10:05 -07:00
);
},
2023-01-02 03:47:05 -08:00
);