Extract shared data grid, selection bar, file dropzone and genre picker components
Track lists and the upload queue now render through a declarative DataGrid, the album editor reuses AlbumForm with a split layout and a track picker, and genre labels and types are shared helpers.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import LibraryAlbumEditor from '$lib/components/music/library/LibraryAlbumEditor.svelte';
|
||||
|
||||
let { data, form } = $props();
|
||||
@@ -9,6 +10,7 @@
|
||||
{#key data.album.id}
|
||||
<LibraryAlbumEditor
|
||||
album={data.album}
|
||||
artist={page.data.user?.name ?? ''}
|
||||
tracks={data.tracks.items}
|
||||
memberIds={data.memberIds}
|
||||
formMessage={form?.message}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import LibraryAlbumEditor from '$lib/components/music/library/LibraryAlbumEditor.svelte';
|
||||
|
||||
let { data, form } = $props();
|
||||
@@ -8,6 +9,7 @@
|
||||
<div class="mt-4">
|
||||
<LibraryAlbumEditor
|
||||
album={null}
|
||||
artist={page.data.user?.name ?? ''}
|
||||
tracks={data.tracks.items}
|
||||
memberIds={[]}
|
||||
formMessage={form?.action === 'saveAlbum' ? form.message : undefined}
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
import Checkbox from '$lib/components/ui/primitives/Checkbox.svelte';
|
||||
import Button from '$lib/components/ui/primitives/Button.svelte';
|
||||
import ExplicitBadge from '$lib/components/ui/media/ExplicitBadge.svelte';
|
||||
import Chip from '$lib/components/ui/primitives/Chip.svelte';
|
||||
import { LIMITS } from '$lib/validation';
|
||||
import { formatSize } from '$lib/utils/format';
|
||||
import { genreOptions } from '$lib/data/genres';
|
||||
import GenrePicker from '$lib/components/music/GenrePicker.svelte';
|
||||
|
||||
type Status = 'idle' | 'uploading' | 'done' | 'error';
|
||||
type StepState = 'idle' | 'active' | 'done';
|
||||
@@ -44,15 +43,6 @@
|
||||
{ key: 'done', label: 'Listo', hint: 'Se procesa y aparece en tu biblioteca.' }
|
||||
];
|
||||
|
||||
const options = $derived(genreOptions(data.genres));
|
||||
const blocked = $derived(
|
||||
new Set(
|
||||
options
|
||||
.filter((option) => tags.includes(option.genre))
|
||||
.flatMap((option) => option.incompatibleWith)
|
||||
)
|
||||
);
|
||||
|
||||
const busy = $derived(status === 'uploading');
|
||||
|
||||
const canPublish = $derived(
|
||||
@@ -91,10 +81,6 @@
|
||||
pickAudio(file);
|
||||
}
|
||||
|
||||
function toggleTag(tag: string) {
|
||||
tags = tags.includes(tag) ? tags.filter((current) => current !== tag) : [...tags, tag];
|
||||
}
|
||||
|
||||
function reset() {
|
||||
title = '';
|
||||
audioName = '';
|
||||
@@ -258,28 +244,12 @@
|
||||
{/snippet}
|
||||
</Field>
|
||||
|
||||
<Field label="Géneros">
|
||||
<div class="mb-2 flex flex-wrap gap-2">
|
||||
{#each options as option (option.genre)}
|
||||
<Chip
|
||||
selected={tags.includes(option.genre)}
|
||||
disabled={blocked.has(option.genre)}
|
||||
onclick={() => toggleTag(option.genre)}
|
||||
>
|
||||
{option.label}
|
||||
</Chip>
|
||||
{/each}
|
||||
</div>
|
||||
{#each tags as tag (tag)}
|
||||
<input type="hidden" name="tags" value={tag} />
|
||||
{/each}
|
||||
{#snippet hint()}
|
||||
<p>
|
||||
Elige al menos uno. Algunos géneros no se pueden combinar, como Metal con Ambient.
|
||||
</p>
|
||||
<span class="shrink-0 tabular-nums">{tags.length} elegidos</span>
|
||||
{/snippet}
|
||||
</Field>
|
||||
<GenrePicker
|
||||
genres={data.genres}
|
||||
bind:selected={tags}
|
||||
name="tags"
|
||||
help="Elige al menos uno. Algunos géneros no se pueden combinar, como Metal con Ambient."
|
||||
/>
|
||||
|
||||
<Checkbox bind:checked={isExplicit} name="isExplicit">
|
||||
<span class="inline-flex items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user