feishin/src/renderer/features/search/components/library-command-item.tsx

218 lines
6.4 KiB
TypeScript
Raw Normal View History

import { Center, Flex } from '@mantine/core';
import { MouseEvent, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
2023-05-19 00:21:36 -07:00
import {
2023-07-01 19:10:05 -07:00
RiAddBoxFill,
RiAddCircleFill,
RiAlbumFill,
RiPlayFill,
RiPlayListFill,
2025-06-09 16:02:03 +07:00
RiShuffleFill,
2023-07-01 19:10:05 -07:00
RiUserVoiceFill,
2023-05-19 00:21:36 -07:00
} from 'react-icons/ri';
import styled from 'styled-components';
2023-05-19 22:24:15 -07:00
import { Button, Text } from '/@/renderer/components';
2025-05-20 19:23:36 -07:00
import { LibraryItem } from '/@/shared/types/domain-types';
import { Play, PlayQueueAddOptions } from '/@/shared/types/types';
2023-05-19 00:21:36 -07:00
2023-05-19 22:24:15 -07:00
const Item = styled(Flex)``;
2023-05-19 00:21:36 -07:00
const ItemGrid = styled.div<{ height: number }>`
2023-07-01 19:10:05 -07:00
display: grid;
grid-template-areas: 'image info';
grid-template-rows: 1fr;
grid-template-columns: ${(props) => props.height}px minmax(0, 1fr);
2023-09-15 20:42:38 -07:00
grid-auto-columns: 1fr;
2023-07-01 19:10:05 -07:00
gap: 0.5rem;
width: 100%;
max-width: 100%;
height: 100%;
letter-spacing: 0.5px;
2023-05-19 00:21:36 -07:00
`;
const ImageWrapper = styled.div`
2023-07-01 19:10:05 -07:00
display: flex;
grid-area: image;
align-items: center;
justify-content: center;
height: 100%;
2023-05-19 00:21:36 -07:00
`;
const MetadataWrapper = styled.div`
2023-07-01 19:10:05 -07:00
display: flex;
flex-direction: column;
grid-area: info;
justify-content: center;
width: 100%;
2023-05-19 00:21:36 -07:00
`;
2025-05-20 19:23:36 -07:00
const StyledImage = styled.img<{ placeholder?: string }>`
object-fit: var(--image-fit);
2023-07-01 19:10:05 -07:00
border-radius: 4px;
2023-05-19 00:21:36 -07:00
`;
2023-05-19 22:24:15 -07:00
const ActionsContainer = styled(Flex)``;
2023-05-19 00:21:36 -07:00
interface LibraryCommandItemProps {
2025-05-07 19:53:23 -07:00
disabled?: boolean;
2023-07-01 19:10:05 -07:00
handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void;
id: string;
imageUrl: null | string;
2023-07-01 19:10:05 -07:00
itemType: LibraryItem;
subtitle?: string;
title?: string;
2023-05-19 00:21:36 -07:00
}
export const LibraryCommandItem = ({
2025-05-07 19:53:23 -07:00
disabled,
handlePlayQueueAdd,
2023-07-01 19:10:05 -07:00
id,
imageUrl,
itemType,
2023-07-01 19:10:05 -07:00
subtitle,
title,
2023-05-19 00:21:36 -07:00
}: LibraryCommandItemProps) => {
const { t } = useTranslation();
2023-07-01 19:10:05 -07:00
let Placeholder = RiAlbumFill;
2023-05-19 00:21:36 -07:00
2023-07-01 19:10:05 -07:00
switch (itemType) {
case LibraryItem.ALBUM:
Placeholder = RiAlbumFill;
break;
case LibraryItem.ALBUM_ARTIST:
2023-07-01 19:10:05 -07:00
Placeholder = RiUserVoiceFill;
break;
case LibraryItem.ARTIST:
2023-07-01 19:10:05 -07:00
Placeholder = RiUserVoiceFill;
break;
case LibraryItem.PLAYLIST:
Placeholder = RiPlayListFill;
break;
default:
Placeholder = RiAlbumFill;
break;
}
2023-05-19 00:21:36 -07:00
2023-07-01 19:10:05 -07:00
const handlePlay = useCallback(
(e: MouseEvent, id: string, playType: Play) => {
e.stopPropagation();
handlePlayQueueAdd?.({
byItemType: {
id: [id],
type: itemType,
},
playType,
});
2023-05-19 00:21:36 -07:00
},
2023-07-01 19:10:05 -07:00
[handlePlayQueueAdd, itemType],
);
2023-05-19 00:21:36 -07:00
2023-07-01 19:10:05 -07:00
return (
<Item
gap="xl"
justify="space-between"
style={{ height: '40px', width: '100%' }}
2023-05-19 00:21:36 -07:00
>
2023-07-01 19:10:05 -07:00
<ItemGrid height={40}>
<ImageWrapper>
{imageUrl ? (
<StyledImage
alt="cover"
height={40}
placeholder="var(--placeholder-bg)"
src={imageUrl}
style={{}}
width={40}
/>
) : (
<Center
style={{
background: 'var(--placeholder-bg)',
borderRadius: 'var(--card-default-radius)',
height: `${40}px`,
width: `${40}px`,
}}
>
<Placeholder
color="var(--placeholder-fg)"
size={35}
/>
</Center>
)}
</ImageWrapper>
<MetadataWrapper>
<Text overflow="hidden">{title}</Text>
<Text
$secondary
overflow="hidden"
>
{subtitle}
</Text>
</MetadataWrapper>
</ItemGrid>
<ActionsContainer
align="center"
gap="sm"
justify="flex-end"
>
<Button
compact
2025-05-07 19:53:23 -07:00
disabled={disabled}
onClick={(e) => handlePlay(e, id, Play.NOW)}
2023-07-01 19:10:05 -07:00
size="md"
tooltip={{
label: t('player.play', { postProcess: 'sentenceCase' }),
openDelay: 500,
}}
2023-07-01 19:10:05 -07:00
variant="default"
>
<RiPlayFill />
</Button>
2025-06-09 16:02:03 +07:00
{itemType !== LibraryItem.SONG && (
<Button
compact
disabled={disabled}
onClick={(e) => handlePlay(e, id, Play.SHUFFLE)}
size="md"
tooltip={{
label: t('player.shuffle', { postProcess: 'sentenceCase' }),
openDelay: 500,
}}
variant="default"
>
<RiShuffleFill />
</Button>
)}
2023-07-01 19:10:05 -07:00
<Button
compact
2025-05-07 19:53:23 -07:00
disabled={disabled}
onClick={(e) => handlePlay(e, id, Play.LAST)}
2023-07-01 19:10:05 -07:00
size="md"
tooltip={{
label: t('player.addLast', { postProcess: 'sentenceCase' }),
openDelay: 500,
}}
2023-07-01 19:10:05 -07:00
variant="default"
>
<RiAddBoxFill />
</Button>
<Button
compact
2025-05-07 19:53:23 -07:00
disabled={disabled}
onClick={(e) => handlePlay(e, id, Play.NEXT)}
2023-07-01 19:10:05 -07:00
size="md"
tooltip={{
label: t('player.addNext', { postProcess: 'sentenceCase' }),
openDelay: 500,
}}
2023-07-01 19:10:05 -07:00
variant="default"
>
<RiAddCircleFill />
</Button>
</ActionsContainer>
</Item>
);
2023-05-19 00:21:36 -07:00
};