Unify web-player design tokens and componentize collection headers
Migrates remaining one-off colors (bg-white/[N%], border-white/N, hardcoded shadows/gradients) to the existing --mf-* tokens so light mode reacts consistently everywhere, including a fix to --color-hover which was hardcoded instead of pointing at --mf-surface-2. Extracts CollectionHeader (album/playlist/mix/liked headers) and AccountMenu (TopBar/MobileHeader) to remove five and two duplicated implementations respectively, and normalizes inconsistent empty-state copy.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte';
|
||||
import BackLink from '$lib/components/ui/BackLink.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -48,45 +49,34 @@
|
||||
label={isOwner ? 'Volver a tus álbumes' : 'Volver al inicio'}
|
||||
/>
|
||||
|
||||
<div class="mt-3 flex flex-col gap-5 sm:mt-4 sm:flex-row sm:items-end sm:gap-6">
|
||||
<PlaylistArt
|
||||
{trackIds}
|
||||
{coverUrl}
|
||||
size="large"
|
||||
class="h-36 w-36 shrink-0 rounded-[18px] shadow-[0_24px_56px_rgba(0,0,0,.55)] sm:h-41 sm:w-41"
|
||||
/>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[10.5px] font-semibold tracking-[0.16em] text-fg-2 uppercase">Álbum</p>
|
||||
<h1
|
||||
class="mt-1.5 font-display text-[28px] font-semibold tracking-[-0.035em] break-words text-fg sm:text-[33px]"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
{#if description}
|
||||
<p class="mt-3 max-w-2xl text-sm text-fg-2 sm:text-base">{description}</p>
|
||||
{/if}
|
||||
<p class="mt-3 text-sm text-muted">{meta}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
|
||||
<Button size="sm" onclick={onPlayAll} disabled={trackIds.length === 0}>
|
||||
{#if playing}
|
||||
<Pause class="h-4 w-4" strokeWidth={1.5} />
|
||||
Pausar
|
||||
{:else}
|
||||
<Play class="h-4 w-4" strokeWidth={1.5} />
|
||||
Reproducir
|
||||
{/if}
|
||||
</Button>
|
||||
{#if isOwner}
|
||||
<Button size="sm" variant="secondary" onclick={onEdit}>
|
||||
<SquarePencil class="h-4 w-4" strokeWidth={1.5} />
|
||||
Editar
|
||||
<CollectionHeader eyebrow="Álbum" {title} {description} {meta}>
|
||||
{#snippet cover()}
|
||||
<PlaylistArt
|
||||
{trackIds}
|
||||
{coverUrl}
|
||||
size="large"
|
||||
class="h-36 w-36 shrink-0 rounded-[18px] shadow-cover-lg sm:h-41 sm:w-41"
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
<Button size="sm" onclick={onPlayAll} disabled={trackIds.length === 0}>
|
||||
{#if playing}
|
||||
<Pause class="h-4 w-4" strokeWidth={1.5} />
|
||||
Pausar
|
||||
{:else}
|
||||
<Play class="h-4 w-4" strokeWidth={1.5} />
|
||||
Reproducir
|
||||
{/if}
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary" onclick={onDelete}>
|
||||
<Trash class="h-4 w-4" strokeWidth={1.5} />
|
||||
Eliminar
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if isOwner}
|
||||
<Button size="sm" variant="secondary" onclick={onEdit}>
|
||||
<SquarePencil class="h-4 w-4" strokeWidth={1.5} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary" onclick={onDelete}>
|
||||
<Trash class="h-4 w-4" strokeWidth={1.5} />
|
||||
Eliminar
|
||||
</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</CollectionHeader>
|
||||
|
||||
+35
-42
@@ -8,6 +8,7 @@
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import Cover from '$lib/components/ui/Cover.svelte';
|
||||
import Alert from '$lib/components/ui/Alert.svelte';
|
||||
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
|
||||
import TrackTable from '$lib/components/ui/TrackTable.svelte';
|
||||
import TrackRow from '$lib/components/ui/TrackRow.svelte';
|
||||
import TrackMeta from '$lib/components/ui/TrackMeta.svelte';
|
||||
@@ -79,48 +80,40 @@
|
||||
<Page>
|
||||
<BackLink href="/explore" label="Volver a explorar" />
|
||||
|
||||
<div class="mt-3 flex flex-col gap-5 sm:mt-4 sm:flex-row sm:items-end sm:gap-6">
|
||||
<Cover
|
||||
trackId={album.albumId}
|
||||
src={album.thumbnailUrl}
|
||||
size="large"
|
||||
alt={album.title}
|
||||
class="h-36 w-36 shrink-0 rounded-[18px] shadow-[0_24px_56px_rgba(0,0,0,.55)] sm:h-41 sm:w-41"
|
||||
oncontextmenu={(e) => (albumMenu = albumContextMenuStateFor(e, 'youtube', album.albumId))}
|
||||
/>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-[10.5px] font-semibold tracking-[0.16em] text-fg-2 uppercase">
|
||||
{album.isSingle ? 'Single' : album.isEp ? 'EP' : 'Álbum'}
|
||||
</p>
|
||||
<h1
|
||||
class="mt-1.5 font-display text-[28px] font-semibold tracking-[-0.035em] break-words text-fg sm:text-[33px]"
|
||||
>
|
||||
{album.title}
|
||||
</h1>
|
||||
<p class="mt-3 text-base text-fg-2">{album.artist}</p>
|
||||
<p class="mt-1 text-sm text-muted">
|
||||
{[
|
||||
album.releaseYear ? String(album.releaseYear) : undefined,
|
||||
`${tracks.length} ${tracks.length === 1 ? 'canción' : 'canciones'}`,
|
||||
fmtTime(Number(data.album.totalDurationSeconds))
|
||||
]
|
||||
.filter((part) => !!part)
|
||||
.join(' · ')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
|
||||
<Button size="sm" onclick={playAll} disabled={queue.length === 0}>
|
||||
{#if isCurrentQueue && player.playing}
|
||||
<Pause class="h-4 w-4" strokeWidth={1.5} />
|
||||
Pausar
|
||||
{:else}
|
||||
<Play class="h-4 w-4" strokeWidth={1.5} />
|
||||
Reproducir
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
<CollectionHeader
|
||||
eyebrow={album.isSingle ? 'Single' : album.isEp ? 'EP' : 'Álbum'}
|
||||
title={album.title}
|
||||
description={album.artist}
|
||||
meta={[
|
||||
album.releaseYear ? String(album.releaseYear) : undefined,
|
||||
`${tracks.length} ${tracks.length === 1 ? 'canción' : 'canciones'}`,
|
||||
fmtTime(Number(data.album.totalDurationSeconds))
|
||||
]
|
||||
.filter((part) => !!part)
|
||||
.join(' · ')}
|
||||
>
|
||||
{#snippet cover()}
|
||||
<Cover
|
||||
trackId={album.albumId}
|
||||
src={album.thumbnailUrl}
|
||||
size="large"
|
||||
alt={album.title}
|
||||
class="h-36 w-36 shrink-0 rounded-[18px] shadow-cover-lg sm:h-41 sm:w-41"
|
||||
oncontextmenu={(e) => (albumMenu = albumContextMenuStateFor(e, 'youtube', album.albumId))}
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
<Button size="sm" onclick={playAll} disabled={queue.length === 0}>
|
||||
{#if isCurrentQueue && player.playing}
|
||||
<Pause class="h-4 w-4" strokeWidth={1.5} />
|
||||
Pausar
|
||||
{:else}
|
||||
<Play class="h-4 w-4" strokeWidth={1.5} />
|
||||
Reproducir
|
||||
{/if}
|
||||
</Button>
|
||||
{/snippet}
|
||||
</CollectionHeader>
|
||||
|
||||
{#if form?.message}
|
||||
<Alert tone="danger" class="mt-4">{form.message}</Alert>
|
||||
|
||||
Reference in New Issue
Block a user