MediaCard now builds its own art (src/trackIds via Artwork size="fill", or a custom art snippet) and its own play button (onPlay, with a scrim and a glass PlayButton on hover) instead of relying on each wrapper passing it a snippet. Replaces AlbumCard, PlaylistCard and MixTile, which are removed along with MediaGrid.svelte (their grids move to the grid-cards/grid-cards-lg/grid-tiles/grid-wide @utility recipes). format.ts gains albumMeta() and playlistMeta() (previously ad-hoc calculations inside each card). Explore's genre tile moves from an inline style= gradient to the genre-tile @utility recipe + --tile-hue. Along the way, fixes the "follower/s" pluralization on the user profile that had been missed. Delta documented: the art's hover-zoom and the scrim+PlayButton (previously exclusive to MixTile) become MediaCard behavior gated on the onPlay prop — they'll apply to any future card that uses it, for consistency. Verified with npm run check/build; no authenticated visual QA (Docker unavailable this session).
62 lines
1.4 KiB
Svelte
62 lines
1.4 KiB
Svelte
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
import Artwork from './Artwork.svelte';
|
|
import PlayButton from './PlayButton.svelte';
|
|
|
|
interface Props {
|
|
href: string;
|
|
title: string;
|
|
subtitle?: string | null;
|
|
src?: string | null;
|
|
trackIds?: (string | number)[];
|
|
onPlay?: (event: MouseEvent) => void;
|
|
art?: Snippet;
|
|
oncontextmenu?: (event: MouseEvent) => void;
|
|
index?: number;
|
|
}
|
|
|
|
let {
|
|
href,
|
|
title,
|
|
subtitle,
|
|
src,
|
|
trackIds = [],
|
|
onPlay,
|
|
art,
|
|
oncontextmenu,
|
|
index = 0
|
|
}: Props = $props();
|
|
</script>
|
|
|
|
<a {href} class="group animate-enter min-w-0 text-left" style="--i:{index}" {oncontextmenu}>
|
|
<div class="relative">
|
|
{#if art}
|
|
{@render art()}
|
|
{:else}
|
|
<Artwork
|
|
{src}
|
|
{trackIds}
|
|
size="fill"
|
|
alt={title}
|
|
class="rounded-art transition duration-300 group-hover:scale-105"
|
|
/>
|
|
{/if}
|
|
{#if onPlay}
|
|
<div
|
|
class="pointer-events-none absolute inset-0 rounded-art bg-linear-to-b from-transparent from-45% to-scrim/40"
|
|
></div>
|
|
<PlayButton
|
|
variant="glass"
|
|
size="sm"
|
|
onclick={onPlay}
|
|
label="Reproducir {title}"
|
|
class="absolute right-2.5 bottom-2.5 opacity-0 transition-opacity duration-150 group-hover:opacity-100"
|
|
/>
|
|
{/if}
|
|
</div>
|
|
<div class="mt-2.5 truncate text-sm text-fg">{title}</div>
|
|
{#if subtitle}
|
|
<div class="mt-0.5 truncate text-xs text-fg-3">{subtitle}</div>
|
|
{/if}
|
|
</a>
|