Button: subtle variants (primary/secondary, left unchanged per the user's call), glass (formerly secondaryOnGlow, with its own font-normal), accent, strong (cta-strong/ink, replacing the `!` overrides on the Home hero), danger. Uses the --color-btn* tokens instead of bg-[var(--mf-btn-...)]. Radius unified to rounded-control. IconButton: adds shape (square/round), size (xs/sm/md/lg), tone (muted/subtle/plain), pressed (aria-pressed + text-accent), href/reload (links), surface (permanent background + hover to surface-hover instead of the generic `hover` alias, which matched surface-2 and gave no contrast). New PlayButton: a single place for the play/pause SVGs, strong/glass variants, as="span" for non-interactive overlays. Replaces TransportControls' main button, the MixTile overlay, and the "Top result" circle in Explore. Migrated to IconButton: close queue, "Create playlist" (Sidebar), Like (TrackInfo, with pressed), queue (PlayerExtras), back (TopBar), login (MobileHeader), and the row actions in tables (remove/add, size xs). BackLink uses size-icon-sm and drops a duplicate hover. Decision (not blocking): remove TopBar's decorative "forward" button and PlayerExtras' decorative AlignJustify icon (visually dead code). TransportControls' buttons (shuffle/prev/next/repeat) are deliberately left unmigrated: they currently have no background or hit-box of their own, and IconButton always applies a fixed-size box — migrating them would have added a visible hover pill that couldn't be verified without Docker this session. Verified with npm run check/build.
109 lines
3.2 KiB
Svelte
109 lines
3.2 KiB
Svelte
<script lang="ts">
|
|
import { enhance } from '$app/forms';
|
|
import Upload from '@lucide/svelte/icons/upload';
|
|
import X from '@lucide/svelte/icons/x';
|
|
import Music from '@lucide/svelte/icons/music';
|
|
import { player, toQueueItems } from '$lib/player/player.svelte';
|
|
import { fmtTime, fmtDate } from '$lib/format';
|
|
import Page from '$lib/components/ui/Page.svelte';
|
|
import PageHeader from '$lib/components/ui/PageHeader.svelte';
|
|
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
|
import Button from '$lib/components/ui/Button.svelte';
|
|
import IconButton from '$lib/components/ui/IconButton.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';
|
|
import TrackIndexCell from '$lib/components/ui/TrackIndexCell.svelte';
|
|
import TrackTitleCell from '$lib/components/ui/TrackTitleCell.svelte';
|
|
|
|
let { data } = $props();
|
|
|
|
const items = $derived(data.tracks.items);
|
|
const total = $derived(Number(data.tracks.totalItemCount));
|
|
|
|
function togglePlay(index: number) {
|
|
player.playOrToggle(toQueueItems(items), index);
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Tus canciones subidas</title>
|
|
<meta name="description" content="Todas tus canciones subidas a Musify." />
|
|
</svelte:head>
|
|
|
|
<Page>
|
|
<PageHeader
|
|
title="Tus canciones subidas"
|
|
subtitle="{total} {total === 1 ? 'canción' : 'canciones'} subida{total === 1 ? '' : 's'}."
|
|
>
|
|
{#snippet actions()}
|
|
{#if items.length > 0}
|
|
<Button href="/upload">
|
|
<Upload class="h-4 w-4" />
|
|
Subir canción
|
|
</Button>
|
|
{/if}
|
|
{/snippet}
|
|
</PageHeader>
|
|
|
|
{#if items.length > 0}
|
|
<TrackTable
|
|
index
|
|
action
|
|
meta={[
|
|
{ label: 'Álbum', width: '76px' },
|
|
{ label: 'Subida', width: '124px' },
|
|
{ label: 'Escuchas', width: '106px' }
|
|
]}
|
|
class="mt-7 sm:mt-8"
|
|
>
|
|
{#each items as track, i (track.id)}
|
|
{@const active = player.current.id === track.id}
|
|
<TrackRow {active}>
|
|
<TrackIndexCell
|
|
index={i}
|
|
{active}
|
|
playing={player.playing}
|
|
onToggle={() => togglePlay(i)}
|
|
/>
|
|
<TrackTitleCell
|
|
trackId={track.id}
|
|
title={track.title}
|
|
{active}
|
|
onClick={() => togglePlay(i)}
|
|
/>
|
|
<TrackMeta class="hidden sm:block">—</TrackMeta>
|
|
<TrackMeta class="hidden sm:block">{fmtDate(track.createdAt)}</TrackMeta>
|
|
<TrackMeta class="hidden sm:block">{Number(track.listensCount)}</TrackMeta>
|
|
<TrackMeta>{fmtTime(Number(track.duration))}</TrackMeta>
|
|
<form
|
|
method="POST"
|
|
action="?/deleteTrack"
|
|
use:enhance={() =>
|
|
async ({ update }) =>
|
|
update()}
|
|
>
|
|
<input type="hidden" name="trackId" value={track.id} />
|
|
<IconButton type="submit" label="Borrar canción" size="xs" revealOnHover>
|
|
<X class="h-3.5 w-3.5" />
|
|
</IconButton>
|
|
</form>
|
|
</TrackRow>
|
|
{/each}
|
|
</TrackTable>
|
|
{:else}
|
|
<EmptyState
|
|
icon={Music}
|
|
title="Todavía no tienes canciones"
|
|
description="Sube la primera y aparecerá aquí lista para reproducir."
|
|
>
|
|
{#snippet actions()}
|
|
<Button href="/upload">
|
|
<Upload class="h-4 w-4" />
|
|
Sube la primera
|
|
</Button>
|
|
{/snippet}
|
|
</EmptyState>
|
|
{/if}
|
|
</Page>
|