125 lines
5.3 KiB
Plaintext
125 lines
5.3 KiB
Plaintext
@namespace DeepDrftPublic.Client.Controls
|
|
@using DeepDrftModels.DTOs
|
|
|
|
@* Shared presentational queue list. Renders the ordered queue with the current track marked, and
|
|
(when Editable) drag-reorder handles + per-row remove controls. This is the single "view" both
|
|
the docked overlay (17.2) and the embedded panel (17.3) consume — one source, multiple views.
|
|
|
|
Purely presentational: owns no data fetch, no player wiring, and no IQueueService mutation of its
|
|
own. Order changes, removals, and row jumps are surfaced to the parent as EventCallbacks; the
|
|
parent calls the queue engine. It runs during prerender without JS interop (MudDropContainer's
|
|
drag work is client-only and inert when no drag occurs). *@
|
|
|
|
@if (Items is { Count: > 0 })
|
|
{
|
|
@if (Editable)
|
|
{
|
|
<MudDropContainer T="QueueRow" @ref="_dropContainer" Items="Rows" ItemsSelector="@((row, zone) => true)"
|
|
ItemDropped="OnItemDropped" Class="deepdrft-queue-list">
|
|
<ChildContent>
|
|
<MudDropZone T="QueueRow" Identifier="queue" Class="deepdrft-queue-zone" AllowReorder="true"/>
|
|
</ChildContent>
|
|
<ItemRenderer>
|
|
@RenderRow(context)
|
|
</ItemRenderer>
|
|
</MudDropContainer>
|
|
}
|
|
else
|
|
{
|
|
<div class="deepdrft-queue-list">
|
|
@foreach (var row in Rows)
|
|
{
|
|
@RenderRow(row)
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@code {
|
|
/// <summary>The ordered tracks to render. Empty/null renders nothing.</summary>
|
|
[Parameter] public IReadOnlyList<TrackDto>? Items { get; set; }
|
|
|
|
/// <summary>
|
|
/// Index of the current track within <see cref="Items"/>, or -1 when none. The matching row is
|
|
/// rendered with a now-playing marker.
|
|
/// </summary>
|
|
[Parameter] public int CurrentIndex { get; set; } = -1;
|
|
|
|
/// <summary>
|
|
/// When true, rows show drag handles and a remove control and reorder is enabled. When false the
|
|
/// list is a read-only display (the embed's fixed-order shared queue).
|
|
/// </summary>
|
|
[Parameter] public bool Editable { get; set; }
|
|
|
|
/// <summary>
|
|
/// Raised when the user reorders a row: <c>(fromIndex, toIndex)</c>. The parent calls
|
|
/// <c>IQueueService.Move</c>. Only fires when <see cref="Editable"/>.
|
|
/// </summary>
|
|
[Parameter] public EventCallback<(int FromIndex, int ToIndex)> OnReorder { get; set; }
|
|
|
|
/// <summary>
|
|
/// Raised when the user removes a row, carrying the row's index. The parent calls
|
|
/// <c>IQueueService.RemoveAt</c>. Only fires when <see cref="Editable"/>.
|
|
/// </summary>
|
|
[Parameter] public EventCallback<int> OnRemove { get; set; }
|
|
|
|
/// <summary>
|
|
/// Raised when the user clicks a row body to jump playback to it, carrying the row's index. The
|
|
/// parent decides whether/how to honour it (e.g. play from that index).
|
|
/// </summary>
|
|
[Parameter] public EventCallback<int> OnJump { get; set; }
|
|
|
|
private MudDropContainer<QueueRow>? _dropContainer;
|
|
|
|
// Index-tagged view rows. The index is the row's position in Items at render time and is the
|
|
// value surfaced to the parent's callbacks — the component never mutates the underlying list.
|
|
private List<QueueRow> Rows =>
|
|
Items is null
|
|
? []
|
|
: Items.Select((track, index) => new QueueRow(index, track)).ToList();
|
|
|
|
private async Task OnItemDropped(MudItemDropInfo<QueueRow> dropInfo)
|
|
{
|
|
var from = dropInfo.Item!.Index;
|
|
var to = dropInfo.IndexInZone;
|
|
// MudDropContainer recomputes the list from the parent's next render; refresh its snapshot so
|
|
// the dragged row snaps back until the parent's Move re-flows the cascaded Items.
|
|
_dropContainer?.Refresh();
|
|
if (from == to) return;
|
|
await OnReorder.InvokeAsync((from, to));
|
|
}
|
|
|
|
private sealed record QueueRow(int Index, TrackDto Track);
|
|
|
|
private RenderFragment RenderRow(QueueRow row) => __builder =>
|
|
{
|
|
var isCurrent = row.Index == CurrentIndex;
|
|
<div class="@($"deepdrft-queue-row{(isCurrent ? " deepdrft-queue-row-current" : "")}")">
|
|
@if (Editable)
|
|
{
|
|
<MudIcon Icon="@Icons.Material.Filled.DragIndicator" Size="Size.Small"
|
|
Class="deepdrft-queue-drag-handle"/>
|
|
}
|
|
<span class="deepdrft-queue-position">@(row.Index + 1)</span>
|
|
<div class="deepdrft-queue-body" @onclick="() => OnJump.InvokeAsync(row.Index)">
|
|
<span class="deepdrft-queue-title">@row.Track.TrackName</span>
|
|
@if (row.Track.Release is { Artist: var artist } && !string.IsNullOrWhiteSpace(artist))
|
|
{
|
|
<span class="deepdrft-queue-artist">@artist</span>
|
|
}
|
|
</div>
|
|
@if (isCurrent)
|
|
{
|
|
<MudIcon Icon="@Icons.Material.Filled.GraphicEq" Size="Size.Small"
|
|
Color="Color.Primary" Class="deepdrft-queue-nowplaying"/>
|
|
}
|
|
@if (Editable)
|
|
{
|
|
<MudIconButton Icon="@Icons.Material.Filled.Close" Size="Size.Small"
|
|
Class="deepdrft-queue-remove" aria-label="Remove from queue"
|
|
OnClick="() => OnRemove.InvokeAsync(row.Index)"/>
|
|
}
|
|
</div>
|
|
};
|
|
}
|