feat(cms): dedicated grid columns for medium-specific row actions
Replace CmsAlbumBrowser's single RowActions slot with a SpecialColumns list (header + per-row cell). Mix renders a Waveform column, Session a Hero column, between Tracks and Actions; Edit/Delete stay rightmost. Child-row colspan now computed from column count. Cut/ALL unchanged.
This commit is contained in:
@@ -33,6 +33,10 @@ else
|
|||||||
<MudTh>Release Date</MudTh>
|
<MudTh>Release Date</MudTh>
|
||||||
<MudTh>Type</MudTh>
|
<MudTh>Type</MudTh>
|
||||||
<MudTh Style="width: 1%; white-space: nowrap;">Tracks</MudTh>
|
<MudTh Style="width: 1%; white-space: nowrap;">Tracks</MudTh>
|
||||||
|
@foreach (var column in SpecialColumns)
|
||||||
|
{
|
||||||
|
<MudTh Style="width: 1%; white-space: nowrap;">@column.Header</MudTh>
|
||||||
|
}
|
||||||
<MudTh Style="width: 1%; white-space: nowrap;">Actions</MudTh>
|
<MudTh Style="width: 1%; white-space: nowrap;">Actions</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
@@ -64,11 +68,14 @@ else
|
|||||||
</MudChip>
|
</MudChip>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd DataLabel="Tracks">@context.TrackCount</MudTd>
|
<MudTd DataLabel="Tracks">@context.TrackCount</MudTd>
|
||||||
|
@foreach (var column in SpecialColumns)
|
||||||
|
{
|
||||||
|
@* One dedicated cell per host-declared special-action column (Mix waveform, Session hero).
|
||||||
|
The Cell fragment recovers its typed row state via the host's RowFor lookup. Sits between
|
||||||
|
Tracks and Actions so the universal Edit/Delete stay rightmost. *@
|
||||||
|
<MudTd DataLabel="@column.Header">@column.Cell(context.Release)</MudTd>
|
||||||
|
}
|
||||||
<MudTd DataLabel="Actions">
|
<MudTd DataLabel="Actions">
|
||||||
@* Medium-specific row action (Session hero, Mix waveform) when a host supplies one;
|
|
||||||
the ALL tab supplies none. Rendered before the shared edit/delete so the medium
|
|
||||||
affordance reads left-to-right ahead of the universal actions. *@
|
|
||||||
@RowActions?.Invoke(context.Release)
|
|
||||||
<MudTooltip Text="Batch Edit">
|
<MudTooltip Text="Batch Edit">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||||
Size="Size.Small"
|
Size="Size.Small"
|
||||||
@@ -88,7 +95,7 @@ else
|
|||||||
@if (context.IsExpanded)
|
@if (context.IsExpanded)
|
||||||
{
|
{
|
||||||
<MudTr>
|
<MudTr>
|
||||||
<MudTd colspan="9" Style="padding: 0;">
|
<MudTd colspan="@ColumnCount" Style="padding: 0;">
|
||||||
@if (context.IsLoading)
|
@if (context.IsLoading)
|
||||||
{
|
{
|
||||||
<MudStack Row="true" AlignItems="AlignItems.Center" Class="pa-2">
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="pa-2">
|
||||||
@@ -126,12 +133,19 @@ else
|
|||||||
[Parameter] public bool IsLoading { get; set; }
|
[Parameter] public bool IsLoading { get; set; }
|
||||||
[Parameter] public EventCallback OnReleasesChanged { get; set; }
|
[Parameter] public EventCallback OnReleasesChanged { get; set; }
|
||||||
|
|
||||||
// Optional per-row, medium-specific action slot (Session hero upload, Mix waveform generate),
|
// Zero or more dedicated, header-labelled special-action columns (Session hero upload, Mix waveform
|
||||||
// rendered in the Actions cell ahead of the shared edit/delete buttons. The ALL tab leaves it
|
// generate), each rendered as its own header cell + per-row cell between the Tracks and Actions
|
||||||
// unset and renders the grid exactly as before. A per-medium host (CmsCut/Session/MixBrowser)
|
// columns. The ALL and Cut tabs leave this empty and render exactly as before — only the standard
|
||||||
// supplies it so the rich grid filtered to one medium keeps that medium's bespoke affordance —
|
// columns plus Edit/Delete. A per-medium host supplies its bespoke affordances here so the rich
|
||||||
// the rich expand/delete/Type-chip/edit logic stays here, single-sourced, rather than forked.
|
// expand/delete/Type-chip/edit logic stays single-sourced in this grid rather than forked.
|
||||||
[Parameter] public RenderFragment<ReleaseDto>? RowActions { get; set; }
|
[Parameter] public IReadOnlyList<SpecialActionColumn> SpecialColumns { get; set; } = Array.Empty<SpecialActionColumn>();
|
||||||
|
|
||||||
|
// Base columns: expand, Art, Album, Artist, Genre, Release Date, Type, Tracks, Actions = 9.
|
||||||
|
private const int BaseColumnCount = 9;
|
||||||
|
|
||||||
|
// Total rendered columns, driving the expanded child-row colspan so it always spans the full table
|
||||||
|
// regardless of how many special-action columns the host declared.
|
||||||
|
private int ColumnCount => BaseColumnCount + SpecialColumns.Count;
|
||||||
|
|
||||||
private List<AlbumRow> _rows = new();
|
private List<AlbumRow> _rows = new();
|
||||||
|
|
||||||
|
|||||||
@@ -52,13 +52,22 @@ else
|
|||||||
protected override ReleaseDto ReleaseOf(MixRow row) => row.Release;
|
protected override ReleaseDto ReleaseOf(MixRow row) => row.Release;
|
||||||
|
|
||||||
// The grid itself — identical in the embedded and standalone contexts. Defined once as a fragment so
|
// The grid itself — identical in the embedded and standalone contexts. Defined once as a fragment so
|
||||||
// both branches above render the same markup without duplication. The waveform generate is the Mix's
|
// both branches above render the same markup without duplication. The Mix declares one dedicated
|
||||||
// medium-specific RowActions content; the grid hands it each release, and RowFor recovers the
|
// "Waveform" special-action column; the grid renders it between Tracks and Actions, handing the cell
|
||||||
// matching MixRow's generate state.
|
// each release, and RowFor recovers the matching MixRow's generate state.
|
||||||
private RenderFragment GridContent => @<CmsAlbumBrowser Releases="Releases"
|
private RenderFragment GridContent => @<CmsAlbumBrowser Releases="Releases"
|
||||||
IsLoading="Loading"
|
IsLoading="Loading"
|
||||||
OnReleasesChanged="ReloadAsync">
|
OnReleasesChanged="ReloadAsync"
|
||||||
<RowActions Context="release">
|
SpecialColumns="_specialColumns" />;
|
||||||
|
|
||||||
|
private IReadOnlyList<SpecialActionColumn> _specialColumns => new[]
|
||||||
|
{
|
||||||
|
new SpecialActionColumn("Waveform", WaveformCell)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Per-row cell for the dedicated "Waveform" column: status icon plus generate/regenerate button with
|
||||||
|
// progress. Recovers the typed MixRow via RowFor; skips rendering for a release not on the page.
|
||||||
|
private RenderFragment<ReleaseDto> WaveformCell => release =>@<text>
|
||||||
@{ var row = RowFor(release); }
|
@{ var row = RowFor(release); }
|
||||||
@if (row is not null)
|
@if (row is not null)
|
||||||
{
|
{
|
||||||
@@ -90,8 +99,7 @@ else
|
|||||||
}
|
}
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
</RowActions>
|
</text>;
|
||||||
</CmsAlbumBrowser>;
|
|
||||||
|
|
||||||
private async Task GenerateWaveformAsync(MixRow row)
|
private async Task GenerateWaveformAsync(MixRow row)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,13 +53,22 @@ else
|
|||||||
protected override ReleaseDto ReleaseOf(SessionRow row) => row.Release;
|
protected override ReleaseDto ReleaseOf(SessionRow row) => row.Release;
|
||||||
|
|
||||||
// The grid itself — identical in the embedded and standalone contexts. Defined once as a fragment so
|
// The grid itself — identical in the embedded and standalone contexts. Defined once as a fragment so
|
||||||
// both branches above render the same markup without duplication. The hero upload is the Session's
|
// both branches above render the same markup without duplication. The Session declares one dedicated
|
||||||
// medium-specific RowActions content; the grid hands it each release, and RowFor recovers the
|
// "Hero" special-action column; the grid renders it between Tracks and Actions, handing the cell each
|
||||||
// matching SessionRow's upload state.
|
// release, and RowFor recovers the matching SessionRow's upload state.
|
||||||
private RenderFragment GridContent => @<CmsAlbumBrowser Releases="Releases"
|
private RenderFragment GridContent => @<CmsAlbumBrowser Releases="Releases"
|
||||||
IsLoading="Loading"
|
IsLoading="Loading"
|
||||||
OnReleasesChanged="ReloadAsync">
|
OnReleasesChanged="ReloadAsync"
|
||||||
<RowActions Context="release">
|
SpecialColumns="_specialColumns" />;
|
||||||
|
|
||||||
|
private IReadOnlyList<SpecialActionColumn> _specialColumns => new[]
|
||||||
|
{
|
||||||
|
new SpecialActionColumn("Hero", HeroCell)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Per-row cell for the dedicated "Hero" column: thumbnail preview plus set/replace upload button with
|
||||||
|
// progress. Recovers the typed SessionRow via RowFor; skips rendering for a release not on the page.
|
||||||
|
private RenderFragment<ReleaseDto> HeroCell => release =>@<text>
|
||||||
@{ var row = RowFor(release); }
|
@{ var row = RowFor(release); }
|
||||||
@if (row is not null)
|
@if (row is not null)
|
||||||
{
|
{
|
||||||
@@ -93,8 +102,7 @@ else
|
|||||||
</ActivatorContent>
|
</ActivatorContent>
|
||||||
</MudFileUpload>
|
</MudFileUpload>
|
||||||
}
|
}
|
||||||
</RowActions>
|
</text>;
|
||||||
</CmsAlbumBrowser>;
|
|
||||||
|
|
||||||
private async Task UploadHeroAsync(SessionRow row, IBrowserFile? file)
|
private async Task UploadHeroAsync(SessionRow row, IBrowserFile? file)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using DeepDrftModels.DTOs;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace DeepDrftManager.Components.Pages.Tracks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dedicated, header-labelled grid column for a medium-specific row affordance (e.g. Mix waveform
|
||||||
|
/// generate, Session hero upload) in <see cref="CmsAlbumBrowser"/>. A per-medium host declares zero or
|
||||||
|
/// more of these; the grid renders one extra header cell and one extra per-row cell for each, positioned
|
||||||
|
/// between the Tracks column and the universal Actions (Edit/Delete) column. The <see cref="Cell"/>
|
||||||
|
/// fragment is handed each release; the host recovers its typed row state via its own RowFor lookup.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Header">Column header label (e.g. "Waveform", "Hero").</param>
|
||||||
|
/// <param name="Cell">Per-row cell content for a given release.</param>
|
||||||
|
public sealed record SpecialActionColumn(string Header, RenderFragment<ReleaseDto> Cell);
|
||||||
Reference in New Issue
Block a user