refactor(split): extract DeepDrftShared.Client RCL with shared atoms

TrackCard, TracksGallery, DDIcons, DeepDrftPalettes (Default+Cms), DeepDrftFontLinks,
and palette CSS tokens extracted. Both hosts and DeepDrftCms reference the shared RCL.
This commit is contained in:
Daniel Harvey
2026-05-19 17:14:23 -04:00
parent 840192fb79
commit 8b8796fc58
22 changed files with 360 additions and 299 deletions
@@ -0,0 +1,6 @@
@* Google Fonts preconnects + stylesheet link for DeepDrft typography
(Cormorant Garamond / Geist Mono / DM Sans). Emitted once per HTML document
from each host's App.razor. *@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,300;1,400&family=Geist+Mono:wght@300;400;500&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;1,9..40,300&display=swap" rel="stylesheet">
@@ -0,0 +1,75 @@
<MudCard Class="deepdrft-track-card-container"
Elevation="4">
@if (!string.IsNullOrEmpty(TrackModel?.ImagePath))
{
<div class="deepdrft-track-card-bg" style="background-image: url('@TrackModel.ImagePath');">
</div>
}
else
{
<MudPaper Class="deepdrft-track-card-fallback mud-theme-secondary"
Elevation="0">
</MudPaper>
}
<MudCardContent Class="deepdrft-track-card-content">
<div class="deepdrft-track-info-top">
<MudText Typo="Typo.subtitle1"
Color="Color.Surface"
Class="text-truncate mb-1">
@TrackModel?.TrackName
</MudText>
<MudText Typo="Typo.caption"
Color="Color.Surface"
Class="text-truncate mb-2">
@TrackModel?.Artist
</MudText>
</div>
<div class="deepdrft-track-info-middle">
@if (!string.IsNullOrEmpty(TrackModel?.Album))
{
<MudText Typo="Typo.caption"
Color="Color.Surface"
Class="text-truncate">
@TrackModel.Album
</MudText>
}
@if (!string.IsNullOrEmpty(TrackModel?.Genre))
{
<MudChip T="string"
Size="Size.Small"
Variant="Variant.Filled"
Color="Color.Primary"
Class="deepdrft-genre-chip">
@TrackModel.Genre
</MudChip>
}
</div>
<div class="deepdrft-track-info-bottom">
@if (TrackModel?.ReleaseDate.HasValue == true)
{
<MudText Typo="Typo.caption"
Color="Color.Surface">
@TrackModel.ReleaseDate.Value.Year
</MudText>
}
else
{
<div></div>
}
<MudFab Color="Color.Primary"
Size="Size.Medium"
StartIcon="@PlayPauseIcon"
OnClick="@PlayClick"/>
</div>
</MudCardContent>
</MudCard>
@@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Components;
using DeepDrftModels.Entities;
using MudBlazor;
namespace DeepDrftShared.Client.Components;
public partial class TrackCard : ComponentBase
{
[Parameter] public required TrackEntity TrackModel { get; set; }
[Parameter] public EventCallback<TrackEntity> OnPlay { get; set; }
[Parameter] public bool IsPlaying { get; set; } = false;
private string PlayPauseIcon => IsPlaying ? Icons.Material.Filled.MusicNote : Icons.Material.Filled.PlayArrow;
private async Task PlayClick()
{
if (!IsPlaying && OnPlay.HasDelegate)
{
await OnPlay.InvokeAsync(TrackModel);
}
}
}
@@ -0,0 +1,12 @@
<MudContainer MaxWidth="MaxWidth.Large" Class="tracks-gallery-container">
<MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var track in Tracks)
{
<MudItem xs="12" sm="6" md="4" lg="3" xl="3">
<div class="deepdrft-track-gallery-item-center">
<TrackCard TrackModel="@track" IsPlaying="@(track.Id == SelectedTrack?.Id)" OnPlay="@HandlePlayClick"/>
</div>
</MudItem>
}
</MudGrid>
</MudContainer>
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Components;
using DeepDrftModels.Entities;
namespace DeepDrftShared.Client.Components;
public partial class TracksGallery : ComponentBase
{
[Parameter] public IEnumerable<TrackEntity> Tracks { get; set; } = [];
[Parameter] public TrackEntity? SelectedTrack { get; set; }
[Parameter] public EventCallback<TrackEntity?> SelectedTrackChanged { get; set; }
private async Task HandlePlayClick(TrackEntity track)
{
if (SelectedTrack == track) return;
SelectedTrack = track;
StateHasChanged();
if (SelectedTrackChanged.HasDelegate)
{
await SelectedTrackChanged.InvokeAsync(track);
}
}
}
@@ -0,0 +1,5 @@
.tracks-gallery-container {
padding: 16px;
height: 100%;
box-sizing: border-box;
}