From 43928ba9b9ced143337237fea56baad2c4855420 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Fri, 25 Sep 2026 01:23:40 +0200 Subject: [PATCH] Animate library tabs, rows, editor track lists, status badges and dropzone Tabs get a sliding indicator that persists across library routes, grid rows flip and collapse on removal, the selection bar and filters crossfade, album tracks move between lists with a crossfade, processing badges pulse and the dropzone scales while dragging. --- web-player/scripts/check-tokens.mjs | 1 + .../components/music/TrackStatusBadge.svelte | 7 ++- .../music/library/AlbumTrackPicker.svelte | 7 ++- .../music/library/LibraryTracksSection.svelte | 50 ++++++++++++------- .../music/library/ManageTrackList.svelte | 1 + .../music/library/TrackPickList.svelte | 16 +++++- .../components/ui/forms/FileDropzone.svelte | 2 +- .../lib/components/ui/media/DataGrid.svelte | 11 +++- .../components/ui/media/StatusBadge.svelte | 9 ++-- .../lib/components/ui/primitives/Tabs.svelte | 35 +++++++++++-- web-player/src/lib/utils/transitions.ts | 22 +++++++- web-player/src/routes/(app)/+layout.svelte | 8 ++- .../src/routes/(app)/library/+layout.svelte | 4 +- .../(app)/library/albums/[id]/+page.svelte | 2 +- .../(app)/library/albums/new/+page.svelte | 2 +- 15 files changed, 137 insertions(+), 40 deletions(-) diff --git a/web-player/scripts/check-tokens.mjs b/web-player/scripts/check-tokens.mjs index 4f2e14e..928ab06 100644 --- a/web-player/scripts/check-tokens.mjs +++ b/web-player/scripts/check-tokens.mjs @@ -15,6 +15,7 @@ const ARBITRARY_WHITELIST = [ /^my-\[\d+d?vh\]$/, // +error.svelte: centrado vertical de página de error /^py-\[\d+d?vh\]$/, // Explorar: centrado vertical de "sin resultados" /^transition-\[filter\]$/, // Explorar: genre-tile hover + /^transition-\[transform,width\]$/, // Tabs: indicador deslizante /^bg-\[image:var\(--mf-[\w-]+\)\]$/ ]; diff --git a/web-player/src/lib/components/music/TrackStatusBadge.svelte b/web-player/src/lib/components/music/TrackStatusBadge.svelte index 1aa83b7..d22ba28 100644 --- a/web-player/src/lib/components/music/TrackStatusBadge.svelte +++ b/web-player/src/lib/components/music/TrackStatusBadge.svelte @@ -13,4 +13,9 @@ const info = $derived(TRACK_STATUS[status]); - + diff --git a/web-player/src/lib/components/music/library/AlbumTrackPicker.svelte b/web-player/src/lib/components/music/library/AlbumTrackPicker.svelte index f322e65..9b84fea 100644 --- a/web-player/src/lib/components/music/library/AlbumTrackPicker.svelte +++ b/web-player/src/lib/components/music/library/AlbumTrackPicker.svelte @@ -4,6 +4,7 @@ import X from '@lucide/svelte/icons/x'; import type { LibraryTrack } from '$lib/types'; import { plural } from '$lib/utils/format'; + import { transferBetweenLists } from '$lib/utils/transitions'; import EmptyState from '$lib/components/ui/primitives/EmptyState.svelte'; import TrackPickList from './TrackPickList.svelte'; @@ -14,6 +15,8 @@ let { tracks, memberIds }: Props = $props(); + const [send, receive] = transferBetweenLists(); + let currentIds = $state(untrack(() => [...memberIds])); const initialIds = untrack(() => new Set(memberIds)); @@ -43,13 +46,13 @@ {#if available.length > 0}

Añadir desde tu biblioteca

- + {/if}
{#if inAlbum.length > 0}

En este álbum

- + {:else} + const FADE = { duration: 200 }; + + {label}{label} diff --git a/web-player/src/lib/components/ui/primitives/Tabs.svelte b/web-player/src/lib/components/ui/primitives/Tabs.svelte index 4e86149..681006a 100644 --- a/web-player/src/lib/components/ui/primitives/Tabs.svelte +++ b/web-player/src/lib/components/ui/primitives/Tabs.svelte @@ -12,16 +12,43 @@ } let { label, tabs, class: klass = '' }: Props = $props(); + + let links = $state>({}); + let indicator = $state({ x: 0, width: 0 }); + let measured = $state(false); + let animated = $state(false); + + const activeHref = $derived(tabs.find((tab) => tab.active)?.href); + + $effect(() => { + const link = activeHref ? links[activeHref] : undefined; + if (!link) return; + indicator = { x: link.offsetLeft, width: link.offsetWidth }; + measured = true; + if (!animated) requestAnimationFrame(() => (animated = true)); + }); -