mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 10:03:33 +00:00
Add additional information to album: record label, release type, version (#1242)
* Add additional information to album * add mbz release types and normalization * update Pill styling --------- Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
parent
3fe6ccf300
commit
e26ffaac53
12 changed files with 223 additions and 40 deletions
|
|
@ -97,6 +97,8 @@
|
||||||
"quit": "quit",
|
"quit": "quit",
|
||||||
"random": "random",
|
"random": "random",
|
||||||
"rating": "rating",
|
"rating": "rating",
|
||||||
|
"recordLabel": "record label",
|
||||||
|
"releaseType": "release type",
|
||||||
"refresh": "refresh",
|
"refresh": "refresh",
|
||||||
"reload": "reload",
|
"reload": "reload",
|
||||||
"reset": "reset",
|
"reset": "reset",
|
||||||
|
|
@ -496,6 +498,29 @@
|
||||||
"pause": "pause",
|
"pause": "pause",
|
||||||
"viewQueue": "view queue"
|
"viewQueue": "view queue"
|
||||||
},
|
},
|
||||||
|
"releaseType": {
|
||||||
|
"primary": {
|
||||||
|
"album": "$t(entity.album_one)",
|
||||||
|
"broadcast": "broadcast",
|
||||||
|
"ep": "ep",
|
||||||
|
"other": "other",
|
||||||
|
"single": "single"
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"audiobook": "audiobook",
|
||||||
|
"audioDrama": "audio drama",
|
||||||
|
"compilation": "compilation",
|
||||||
|
"djMix": "dj mix",
|
||||||
|
"demo": "demo",
|
||||||
|
"fieldRecording": "field recording",
|
||||||
|
"interview": "interview",
|
||||||
|
"live": "live",
|
||||||
|
"mixtape": "mixtape",
|
||||||
|
"remix": "remix",
|
||||||
|
"soundtrack": "soundtrack",
|
||||||
|
"spokenWord": "spoken word"
|
||||||
|
}
|
||||||
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
"accentColor_description": "sets the accent color for the application",
|
"accentColor_description": "sets the accent color for the application",
|
||||||
"accentColor": "accent color",
|
"accentColor": "accent color",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { forwardRef, Fragment, Ref, useCallback, useMemo } from 'react';
|
import { forwardRef, Ref, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { generatePath, useParams } from 'react-router';
|
import { generatePath, useParams } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
@ -13,8 +13,10 @@ import { useSongChange } from '/@/renderer/hooks/use-song-change';
|
||||||
import { queryClient } from '/@/renderer/lib/react-query';
|
import { queryClient } from '/@/renderer/lib/react-query';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils';
|
import { formatDateAbsoluteUTC, formatDurationString, titleCase } from '/@/renderer/utils';
|
||||||
|
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
|
import { Pill } from '/@/shared/components/pill/pill';
|
||||||
import { Rating } from '/@/shared/components/rating/rating';
|
import { Rating } from '/@/shared/components/rating/rating';
|
||||||
import { Stack } from '/@/shared/components/stack/stack';
|
import { Stack } from '/@/shared/components/stack/stack';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
|
|
@ -38,7 +40,9 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
const cq = useContainerQuery();
|
const cq = useContainerQuery();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME;
|
const showRating =
|
||||||
|
detailQuery?.data?.serverType === ServerType.NAVIDROME ||
|
||||||
|
detailQuery?.data?.serverType === ServerType.SUBSONIC;
|
||||||
|
|
||||||
const originalDifferentFromRelease =
|
const originalDifferentFromRelease =
|
||||||
detailQuery.data?.originalDate &&
|
detailQuery.data?.originalDate &&
|
||||||
|
|
@ -78,7 +82,16 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
}
|
}
|
||||||
}, detailQuery.data !== undefined);
|
}, detailQuery.data !== undefined);
|
||||||
|
|
||||||
const metadataItems = [
|
const releaseTypes = useMemo(
|
||||||
|
() =>
|
||||||
|
normalizeReleaseTypes(detailQuery.data?.releaseTypes ?? [], t).map((type) => ({
|
||||||
|
id: type,
|
||||||
|
value: titleCase(type),
|
||||||
|
})) || [],
|
||||||
|
[detailQuery.data?.releaseTypes, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const metadataItems = releaseTypes.concat([
|
||||||
{
|
{
|
||||||
id: 'releaseDate',
|
id: 'releaseDate',
|
||||||
value:
|
value:
|
||||||
|
|
@ -98,11 +111,17 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'playCount',
|
id: 'playCount',
|
||||||
value: t('entity.play', {
|
value:
|
||||||
count: detailQuery?.data?.playCount as number,
|
typeof detailQuery?.data?.playCount === 'number' &&
|
||||||
}),
|
t('entity.play', {
|
||||||
|
count: detailQuery?.data?.playCount,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
];
|
{
|
||||||
|
id: 'version',
|
||||||
|
value: detailQuery.data?.version,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
if (originalDifferentFromRelease) {
|
if (originalDifferentFromRelease) {
|
||||||
const formatted = `♫ ${formatDateAbsoluteUTC(detailQuery!.data!.originalDate)}`;
|
const formatted = `♫ ${formatDateAbsoluteUTC(detailQuery!.data!.originalDate)}`;
|
||||||
|
|
@ -135,28 +154,22 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
title={detailQuery?.data?.name || ''}
|
title={detailQuery?.data?.name || ''}
|
||||||
{...background}
|
{...background}
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="lg">
|
||||||
<Group gap="sm">
|
<Pill.Group>
|
||||||
{metadataItems.map((item, index) => (
|
{metadataItems.map(
|
||||||
<Fragment key={`item-${item.id}-${index}`}>
|
(item, index) =>
|
||||||
{index > 0 && <Text isNoSelect>•</Text>}
|
item.value && (
|
||||||
<Text>{item.value}</Text>
|
<Pill key={`item-${item.id}-${index}`}>{item.value}</Pill>
|
||||||
</Fragment>
|
),
|
||||||
))}
|
|
||||||
{showRating && (
|
|
||||||
<>
|
|
||||||
<Text isNoSelect>•</Text>
|
|
||||||
<Rating
|
|
||||||
onChange={handleUpdateRating}
|
|
||||||
readOnly={
|
|
||||||
detailQuery?.isFetching ||
|
|
||||||
updateRatingMutation.isPending
|
|
||||||
}
|
|
||||||
value={detailQuery?.data?.userRating || 0}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Pill.Group>
|
||||||
|
{showRating && (
|
||||||
|
<Rating
|
||||||
|
onChange={handleUpdateRating}
|
||||||
|
readOnly={detailQuery?.isFetching || updateRatingMutation.isPending}
|
||||||
|
value={detailQuery?.data?.userRating || 0}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Group
|
<Group
|
||||||
gap="md"
|
gap="md"
|
||||||
mah="4rem"
|
mah="4rem"
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { formatDurationString, formatSizeString } from '/@/renderer/utils';
|
import { formatDurationString, formatSizeString } from '/@/renderer/utils';
|
||||||
import { formatDateRelative, formatRating } from '/@/renderer/utils/format';
|
import { formatDateRelative, formatRating } from '/@/renderer/utils/format';
|
||||||
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
||||||
|
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
|
||||||
import { sanitize } from '/@/renderer/utils/sanitize';
|
import { sanitize } from '/@/renderer/utils/sanitize';
|
||||||
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
|
|
@ -122,6 +123,10 @@ const BoolField = (key: boolean) =>
|
||||||
const AlbumPropertyMapping: ItemDetailRow<Album>[] = [
|
const AlbumPropertyMapping: ItemDetailRow<Album>[] = [
|
||||||
{ key: 'name', label: 'common.title' },
|
{ key: 'name', label: 'common.title' },
|
||||||
{ label: 'entity.albumArtist_one', render: (item) => formatArtists(item.albumArtists) },
|
{ label: 'entity.albumArtist_one', render: (item) => formatArtists(item.albumArtists) },
|
||||||
|
{
|
||||||
|
label: 'common.releaseType',
|
||||||
|
render: (item, t) => normalizeReleaseTypes(item.releaseTypes, t).join(SEPARATOR_STRING),
|
||||||
|
},
|
||||||
{ label: 'entity.genre_other', render: FormatGenre },
|
{ label: 'entity.genre_other', render: FormatGenre },
|
||||||
{
|
{
|
||||||
label: 'common.duration',
|
label: 'common.duration',
|
||||||
|
|
@ -174,6 +179,8 @@ const AlbumPropertyMapping: ItemDetailRow<Album>[] = [
|
||||||
) : null,
|
) : null,
|
||||||
},
|
},
|
||||||
{ key: 'id', label: 'filter.id' },
|
{ key: 'id', label: 'filter.id' },
|
||||||
|
{ key: 'version', label: 'common.version' },
|
||||||
|
{ label: 'common.recordLabel', render: (item) => item.recordLabels.join(SEPARATOR_STRING) },
|
||||||
];
|
];
|
||||||
|
|
||||||
const AlbumArtistPropertyMapping: ItemDetailRow<AlbumArtist>[] = [
|
const AlbumArtistPropertyMapping: ItemDetailRow<AlbumArtist>[] = [
|
||||||
|
|
|
||||||
57
src/renderer/utils/normalize-release-types.tsx
Normal file
57
src/renderer/utils/normalize-release-types.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { TFunction } from 'react-i18next';
|
||||||
|
|
||||||
|
import { titleCase } from '/@/renderer/utils/title-case';
|
||||||
|
|
||||||
|
// Release types derived from https://musicbrainz.org/doc/Release_Group/Type
|
||||||
|
const PRIMARY_MAPPING = {
|
||||||
|
album: 'album',
|
||||||
|
broadcast: 'broadcast',
|
||||||
|
ep: 'ep',
|
||||||
|
other: 'other',
|
||||||
|
single: 'single',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const SECONDARY_MAPPING = {
|
||||||
|
audiobook: 'audiobook',
|
||||||
|
'audio drama': 'audioDrama',
|
||||||
|
compilation: 'compilation',
|
||||||
|
demo: 'demo',
|
||||||
|
'dj-mix': 'djMix',
|
||||||
|
'field recording': 'fieldRecording',
|
||||||
|
interview: 'interview',
|
||||||
|
live: 'live',
|
||||||
|
'mixtape/street': 'mixtape',
|
||||||
|
remix: 'remix',
|
||||||
|
soundtrack: 'soundtrack',
|
||||||
|
spokenword: 'spokenWord',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const normalizeReleaseTypes = (types: string[], t: TFunction) => {
|
||||||
|
const primary: string[] = [];
|
||||||
|
const secondary: string[] = [];
|
||||||
|
const unknown: string[] = [];
|
||||||
|
|
||||||
|
for (const type of types) {
|
||||||
|
const lower = type.toLocaleLowerCase();
|
||||||
|
|
||||||
|
if (lower in PRIMARY_MAPPING) {
|
||||||
|
primary.push(
|
||||||
|
t(`releaseType.primary.${PRIMARY_MAPPING[lower]}`, { postProcess: 'sentenceCase' }),
|
||||||
|
);
|
||||||
|
} else if (lower in SECONDARY_MAPPING) {
|
||||||
|
secondary.push(
|
||||||
|
t(`releaseType.secondary.${SECONDARY_MAPPING[lower]}`, {
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unknown.push(titleCase(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
primary.sort();
|
||||||
|
secondary.sort();
|
||||||
|
unknown.sort();
|
||||||
|
|
||||||
|
return primary.concat(secondary, unknown);
|
||||||
|
};
|
||||||
|
|
@ -340,7 +340,9 @@ const normalizeAlbum = (
|
||||||
originalDate: null,
|
originalDate: null,
|
||||||
participants: getPeople(item),
|
participants: getPeople(item),
|
||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
|
recordLabels: [],
|
||||||
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
||||||
|
releaseTypes: [],
|
||||||
releaseYear: item.ProductionYear || null,
|
releaseYear: item.ProductionYear || null,
|
||||||
serverId: server?.id || '',
|
serverId: server?.id || '',
|
||||||
serverType: ServerType.JELLYFIN,
|
serverType: ServerType.JELLYFIN,
|
||||||
|
|
@ -352,6 +354,7 @@ const normalizeAlbum = (
|
||||||
updatedAt: item?.DateLastMediaAdded || item.DateCreated,
|
updatedAt: item?.DateLastMediaAdded || item.DateCreated,
|
||||||
userFavorite: item.UserData?.IsFavorite || false,
|
userFavorite: item.UserData?.IsFavorite || false,
|
||||||
userRating: null,
|
userRating: null,
|
||||||
|
version: null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,47 @@ const normalizeSong = (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseAlbumTags = (
|
||||||
|
item: z.infer<typeof ndType._response.album>,
|
||||||
|
): Pick<Album, 'recordLabels' | 'releaseTypes' | 'tags' | 'version'> => {
|
||||||
|
if (!item.tags) {
|
||||||
|
return {
|
||||||
|
recordLabels: [],
|
||||||
|
releaseTypes: [],
|
||||||
|
tags: null,
|
||||||
|
version: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// We get the genre from elsewhere. We don't need genre twice
|
||||||
|
delete item.tags['genre'];
|
||||||
|
|
||||||
|
let recordLabels: string[] = [];
|
||||||
|
if (item.tags['recordlabel']) {
|
||||||
|
recordLabels = item.tags['recordlabel'];
|
||||||
|
delete item.tags['recordlabel'];
|
||||||
|
}
|
||||||
|
|
||||||
|
let releaseTypes: string[] = [];
|
||||||
|
if (item.tags['releasetype']) {
|
||||||
|
releaseTypes = item.tags['releasetype'];
|
||||||
|
delete item.tags['releasetype'];
|
||||||
|
}
|
||||||
|
|
||||||
|
let version: null | string = null;
|
||||||
|
if (item.tags['albumversion']) {
|
||||||
|
version = item.tags['albumversion'].join(' · ');
|
||||||
|
delete item.tags['albumversion'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
recordLabels,
|
||||||
|
releaseTypes,
|
||||||
|
tags: item.tags,
|
||||||
|
version,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeAlbum = (
|
const normalizeAlbum = (
|
||||||
item: z.infer<typeof ndType._response.album> & {
|
item: z.infer<typeof ndType._response.album> & {
|
||||||
songs?: z.infer<typeof ndType._response.songList>;
|
songs?: z.infer<typeof ndType._response.songList>;
|
||||||
|
|
@ -238,8 +279,9 @@ const normalizeAlbum = (
|
||||||
const imageBackdropUrl = imageUrl?.replace(/size=\d+/, 'size=1000') || null;
|
const imageBackdropUrl = imageUrl?.replace(/size=\d+/, 'size=1000') || null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
albumArtist: item.albumArtist,
|
...parseAlbumTags(item),
|
||||||
...getArtists(item),
|
...getArtists(item),
|
||||||
|
albumArtist: item.albumArtist,
|
||||||
backdropImageUrl: imageBackdropUrl,
|
backdropImageUrl: imageBackdropUrl,
|
||||||
comment: item.comment || null,
|
comment: item.comment || null,
|
||||||
createdAt: item.createdAt.split('T')[0],
|
createdAt: item.createdAt.split('T')[0],
|
||||||
|
|
@ -281,7 +323,6 @@ const normalizeAlbum = (
|
||||||
size: item.size,
|
size: item.size,
|
||||||
songCount: item.songCount,
|
songCount: item.songCount,
|
||||||
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
||||||
tags: item.tags || null,
|
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.updatedAt,
|
updatedAt: item.updatedAt,
|
||||||
userFavorite: item.starred,
|
userFavorite: item.starred,
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,9 @@ const normalizeAlbum = (
|
||||||
originalDate: null,
|
originalDate: null,
|
||||||
participants: getParticipants(item),
|
participants: getParticipants(item),
|
||||||
playCount: null,
|
playCount: null,
|
||||||
|
recordLabels: item.recordLabels?.map((item) => item.name) || [],
|
||||||
releaseDate: item.year ? new Date(Date.UTC(item.year, 0, 1)).toISOString() : null,
|
releaseDate: item.year ? new Date(Date.UTC(item.year, 0, 1)).toISOString() : null,
|
||||||
|
releaseTypes: item.releaseTypes || [],
|
||||||
releaseYear: item.year ? Number(item.year) : null,
|
releaseYear: item.year ? Number(item.year) : null,
|
||||||
serverId: server?.id || 'unknown',
|
serverId: server?.id || 'unknown',
|
||||||
serverType: ServerType.SUBSONIC,
|
serverType: ServerType.SUBSONIC,
|
||||||
|
|
@ -287,6 +289,7 @@ const normalizeAlbum = (
|
||||||
updatedAt: item.created,
|
updatedAt: item.created,
|
||||||
userFavorite: item.starred || false,
|
userFavorite: item.starred || false,
|
||||||
userRating: item.userRating || null,
|
userRating: item.userRating || null,
|
||||||
|
version: item.version || null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,10 @@ const song = z.object({
|
||||||
year: z.number().optional(),
|
year: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const recordLabel = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
const album = z.object({
|
const album = z.object({
|
||||||
album: z.string(),
|
album: z.string(),
|
||||||
artist: z.string(),
|
artist: z.string(),
|
||||||
|
|
@ -135,11 +139,14 @@ const album = z.object({
|
||||||
isVideo: z.boolean(),
|
isVideo: z.boolean(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
parent: z.string(),
|
parent: z.string(),
|
||||||
|
recordLabels: z.array(recordLabel).optional(),
|
||||||
|
releaseTypes: z.array(z.string()).optional(),
|
||||||
song: z.array(song),
|
song: z.array(song),
|
||||||
songCount: z.number(),
|
songCount: z.number(),
|
||||||
starred: z.boolean().optional(),
|
starred: z.boolean().optional(),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
userRating: z.number().optional(),
|
userRating: z.number().optional(),
|
||||||
|
version: z.string().optional(),
|
||||||
year: z.number().optional(),
|
year: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,39 @@
|
||||||
|
.root {
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: auto;
|
||||||
|
background: alpha(var(--theme-colors-background), 0.5);
|
||||||
|
border: 1px solid var(--theme-colors-border);
|
||||||
|
|
||||||
|
&[data-variant='outline'] {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--theme-colors-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-family: var(--theme-content-font-family);
|
font-family: var(--theme-content-font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.sm {
|
.label.sm {
|
||||||
font-size: var(--theme-font-size-sm);
|
font-size: var(--theme-font-size-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.md {
|
.label.md {
|
||||||
font-size: var(--theme-font-size-md);
|
font-size: var(--theme-font-size-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.lg {
|
.label.lg {
|
||||||
font-size: var(--theme-font-size-lg);
|
font-size: var(--theme-font-size-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.xl {
|
.label.xl {
|
||||||
font-size: var(--theme-font-size-xl);
|
font-size: var(--theme-font-size-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.xs {
|
.label.xs {
|
||||||
font-size: var(--theme-font-size-xs);
|
font-size: var(--theme-font-size-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.remove {
|
.remove {
|
||||||
transition: color 0.1s ease-in-out;
|
transition: color 0.1s ease-in-out;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import styles from './pill.module.css';
|
import styles from './pill.module.css';
|
||||||
|
|
||||||
export const Pill = ({ children, size = 'md', ...props }: MantinePillProps) => {
|
interface PillProps extends MantinePillProps {}
|
||||||
|
|
||||||
|
export const Pill = ({ children, classNames, radius = 'md', size = 'md', ...props }: PillProps) => {
|
||||||
return (
|
return (
|
||||||
<MantinePill
|
<MantinePill
|
||||||
classNames={{
|
classNames={{
|
||||||
|
|
@ -17,8 +19,10 @@ export const Pill = ({ children, size = 'md', ...props }: MantinePillProps) => {
|
||||||
}),
|
}),
|
||||||
remove: styles.remove,
|
remove: styles.remove,
|
||||||
root: styles.root,
|
root: styles.root,
|
||||||
|
...classNames,
|
||||||
}}
|
}}
|
||||||
size="md"
|
radius={radius}
|
||||||
|
size={size}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,9 @@ export type Album = {
|
||||||
originalDate: null | string;
|
originalDate: null | string;
|
||||||
participants: null | Record<string, RelatedArtist[]>;
|
participants: null | Record<string, RelatedArtist[]>;
|
||||||
playCount: null | number;
|
playCount: null | number;
|
||||||
|
recordLabels: string[];
|
||||||
releaseDate: null | string;
|
releaseDate: null | string;
|
||||||
|
releaseTypes: string[];
|
||||||
releaseYear: null | number;
|
releaseYear: null | number;
|
||||||
serverId: string;
|
serverId: string;
|
||||||
serverType: ServerType;
|
serverType: ServerType;
|
||||||
|
|
@ -194,6 +196,7 @@ export type Album = {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
userFavorite: boolean;
|
userFavorite: boolean;
|
||||||
userRating: null | number;
|
userRating: null | number;
|
||||||
|
version: null | string;
|
||||||
} & { songs?: Song[] };
|
} & { songs?: Song[] };
|
||||||
|
|
||||||
export type AlbumArtist = {
|
export type AlbumArtist = {
|
||||||
|
|
|
||||||
9
src/types/mantine.d.ts
vendored
Normal file
9
src/types/mantine.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { PillVariant } from '@mantine/core';
|
||||||
|
|
||||||
|
type ExtendedPillVariant = 'outline' | PillVariant;
|
||||||
|
|
||||||
|
declare module '@mantine/core' {
|
||||||
|
export interface PillProps {
|
||||||
|
variant?: ExtendedPillVariant;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue