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:
daniel-c-harvey
2026-06-15 11:16:15 -04:00
parent 4df2b8fb57
commit 5941f1f23a
4 changed files with 126 additions and 81 deletions
@@ -52,46 +52,54 @@ else
protected override ReleaseDto ReleaseOf(MixRow row) => row.Release;
// 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
// medium-specific RowActions content; the grid hands it each release, and RowFor recovers the
// matching MixRow's generate state.
// both branches above render the same markup without duplication. The Mix declares one dedicated
// "Waveform" special-action column; the grid renders it between Tracks and Actions, handing the cell
// each release, and RowFor recovers the matching MixRow's generate state.
private RenderFragment GridContent => @<CmsAlbumBrowser Releases="Releases"
IsLoading="Loading"
OnReleasesChanged="ReloadAsync">
<RowActions Context="release">
@{ var row = RowFor(release); }
@if (row is not null)
OnReleasesChanged="ReloadAsync"
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); }
@if (row is not null)
{
@if (row.HasWaveform)
{
@if (row.HasWaveform)
<MudTooltip Text="Waveform generated">
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Small" />
</MudTooltip>
}
else
{
<MudTooltip Text="No waveform — incomplete">
<MudIcon Icon="@Icons.Material.Filled.Cancel" Color="Color.Warning" Size="Size.Small" />
</MudTooltip>
}
<MudButton Variant="Variant.Outlined"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.GraphicEq"
Disabled="@row.IsGenerating"
OnClick="@(() => GenerateWaveformAsync(row))">
@if (row.IsGenerating)
{
<MudTooltip Text="Waveform generated">
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Small" />
</MudTooltip>
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
<span>Generating…</span>
}
else
{
<MudTooltip Text="No waveform — incomplete">
<MudIcon Icon="@Icons.Material.Filled.Cancel" Color="Color.Warning" Size="Size.Small" />
</MudTooltip>
<span>@(row.HasWaveform ? "Regenerate" : "Generate")</span>
}
<MudButton Variant="Variant.Outlined"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.GraphicEq"
Disabled="@row.IsGenerating"
OnClick="@(() => GenerateWaveformAsync(row))">
@if (row.IsGenerating)
{
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
<span>Generating…</span>
}
else
{
<span>@(row.HasWaveform ? "Regenerate" : "Generate")</span>
}
</MudButton>
}
</RowActions>
</CmsAlbumBrowser>;
</MudButton>
}
</text>;
private async Task GenerateWaveformAsync(MixRow row)
{