Crea src/lib/actions/pressable.ts y clickOutside.ts (Svelte actions; role/tabindex se mantienen estáticos en el markup por accesibilidad SSR, la acción solo añade click + Enter/Espacio) y src/lib/toggle.svelte.ts (createToggle), usado por playlists.svelte.ts y queuePanel.svelte.ts. format.ts: añade plural(n, uno, muchos); migra las 15 pluralizaciones manuales del repo, unificando a minúscula (Δ visible en Playlists, Inicio, liked, Explorar). player.svelte.ts: añade toQueueItem, isQueueCurrent, playAllOrToggle, playShuffled; renombra addToQueue → playNextItem (el nombre anterior confundía: inserta a continuación, no al final). Migra los 5 sitios que repetían el patrón isCurrentQueue/playAll (álbum, playlist, liked, mezcla, Inicio). Simplifica liked.svelte.ts y recentlyPlayed.ts con object spread en vez de reconstruir cada campo a mano. navigation.svelte.ts: añade searchHref (migra el buildHref duplicado de TopBar y Explorar) y appNavLinks (creado; su adopción en Sidebar/ TabsBar queda para P12, como indica esa parte). menus.svelte.ts: createTrackMenu() sustituye el estado manual de menú contextual (contextMenu + contextMenuStateFor + addToQueue) en Inicio, Explorar y Mezcla. Pendiente a propósito: la tarea 5 (eliminar el envoltorio TrackTarget de tracks.ts y sus 8 accesores) se difiere a P13 — es el cambio de mayor riesgo del plan, toca tipos de forma transversal en Inicio/ Explorar/Mezcla/menús, y no se pudo verificar visualmente sin Docker en esta sesión. Verificado con npm run check/build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
63 lines
1.4 KiB
Svelte
63 lines
1.4 KiB
Svelte
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
import Artwork from './Artwork.svelte';
|
|
import ExplicitBadge from './ExplicitBadge.svelte';
|
|
import ArtistLink from './ArtistLink.svelte';
|
|
import { pressable } from '$lib/actions/pressable';
|
|
|
|
interface Props {
|
|
trackId: string | number;
|
|
title: string;
|
|
artist?: string | null;
|
|
ownerUserId?: string | number | null;
|
|
coverSrc?: string | null;
|
|
size?: 'sm' | 'lg';
|
|
explicit?: boolean;
|
|
active?: boolean;
|
|
titleClass?: string;
|
|
onClick: () => void;
|
|
overlay?: Snippet;
|
|
badge?: Snippet;
|
|
}
|
|
|
|
let {
|
|
trackId,
|
|
title,
|
|
artist,
|
|
ownerUserId,
|
|
coverSrc,
|
|
size = 'sm',
|
|
explicit = false,
|
|
active = false,
|
|
titleClass = '',
|
|
onClick,
|
|
overlay,
|
|
badge
|
|
}: Props = $props();
|
|
</script>
|
|
|
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
<div
|
|
role="button"
|
|
tabindex="0"
|
|
use:pressable={onClick}
|
|
onclick={(event) => event.stopPropagation()}
|
|
class="flex min-w-0 items-center gap-3.5 text-left"
|
|
>
|
|
<Artwork trackIds={[trackId]} src={coverSrc} {size} alt={title} class="shrink-0">
|
|
{@render overlay?.()}
|
|
</Artwork>
|
|
<div class="min-w-0">
|
|
<div class="flex min-w-0 items-center gap-2">
|
|
{#if explicit}
|
|
<ExplicitBadge />
|
|
{/if}
|
|
<span class="min-w-0 truncate text-sm {active ? 'text-accent-soft' : 'text-fg'} {titleClass}"
|
|
>{title}</span
|
|
>
|
|
{@render badge?.()}
|
|
</div>
|
|
<ArtistLink name={artist} {ownerUserId} class="mt-0.5 text-xs text-fg-3" />
|
|
</div>
|
|
</div>
|