MediaCard pasa a construir su propio arte (src/trackIds vía Artwork size="fill", o un snippet art a medida) y su botón de reproducir (onPlay, con scrim y PlayButton glass en hover) en vez de depender de que cada wrapper le pase un snippet. Sustituye a AlbumCard, PlaylistCard y MixTile, que se borran junto con MediaGrid.svelte (sus grids pasan a los @utility grid-cards/grid-cards-lg/grid-tiles/ grid-wide de P1). format.ts gana albumMeta() y playlistMeta() (antes cálculos ad-hoc dentro de cada tarjeta). El tile de género en Explorar pasa de un style= con el gradiente inline a la receta @utility genre-tile + --tile-hue. De paso, corrige la pluralización "seguidor/es" del perfil de usuario que quedó suelta. Delta documentado: el hover-zoom del arte y el scrim+PlayButton (antes exclusivos de MixTile) pasan a ser comportamiento de MediaCard condicionado a la prop onPlay — se aplicarían a cualquier tarjeta futura que la use, por consistencia. Verificado con npm run check/build; sin QA visual autenticada (Docker no disponible en esta sesión). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
64 lines
1.9 KiB
Svelte
64 lines
1.9 KiB
Svelte
<script lang="ts">
|
|
import ListMusic from '@lucide/svelte/icons/list-music';
|
|
import Page from '$lib/components/ui/Page.svelte';
|
|
import PageHeader from '$lib/components/ui/PageHeader.svelte';
|
|
import MediaCard from '$lib/components/ui/MediaCard.svelte';
|
|
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
|
import Button from '$lib/components/ui/Button.svelte';
|
|
import { createPlaylistModal } from '$lib/playlists.svelte';
|
|
import { fmtDurationLong, plural, playlistMeta } from '$lib/format';
|
|
|
|
let { data } = $props();
|
|
|
|
const items = $derived(data.playlists.items);
|
|
const totals = $derived(data.totals);
|
|
const summary = $derived(
|
|
`${plural(totals.playlistCount, 'playlist', 'playlists')} · ${plural(totals.trackCount, 'canción', 'canciones')} · ${fmtDurationLong(totals.durationSeconds)}`
|
|
);
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Mis listas</title>
|
|
<meta name="description" content="Mis listas en Musify." />
|
|
</svelte:head>
|
|
|
|
<Page>
|
|
<PageHeader
|
|
title="Mis listas"
|
|
subtitle={items.length > 0 ? summary : 'Crea y organiza tus colecciones.'}
|
|
>
|
|
{#snippet actions()}
|
|
{#if items.length > 0}
|
|
<Button onclick={() => createPlaylistModal.show()}>Crear nueva lista</Button>
|
|
{/if}
|
|
{/snippet}
|
|
</PageHeader>
|
|
|
|
{#if items.length > 0}
|
|
<div class="mt-7 grid-cards-lg sm:mt-8">
|
|
{#each items as playlist, i (playlist.id)}
|
|
<MediaCard
|
|
href="/playlists/{playlist.id}"
|
|
title={playlist.name}
|
|
subtitle={playlistMeta(Number(playlist.trackCount))}
|
|
trackIds={playlist.coverTrackIds}
|
|
src="/api/playlists/{playlist.id}/cover?size=large&v={encodeURIComponent(
|
|
playlist.updatedAt
|
|
)}"
|
|
index={i}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<EmptyState
|
|
icon={ListMusic}
|
|
title="Todavía no tienes listas"
|
|
description="Crea la primera y añádele canciones de tu biblioteca."
|
|
>
|
|
{#snippet actions()}
|
|
<Button onclick={() => createPlaylistModal.show()}>Crear nueva lista</Button>
|
|
{/snippet}
|
|
</EmptyState>
|
|
{/if}
|
|
</Page>
|