Skip to content

Commit

Permalink
Lot's of improvements for MudBlazor components.
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix-CodingClimber committed Jan 31, 2024
1 parent 1ba8335 commit 7300da2
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 15 deletions.
72 changes: 72 additions & 0 deletions src/DotNetElements.Web.MudBlazor/AuditedModelDetailsRow.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@inherits MudComponentBase

@typeparam TKey where TKey : notnull, IEquatable<TKey>
@typeparam TModel where TModel : IModel<TKey>
@typeparam TDetails where TDetails : AuditedModelDetails

@if (Context.DetailsShown && Context.Details is not null)
{
@if (SimpleTable)
{
<tr>
<td colspan="100">
<MudGrid Spacing="2">
<MudItem xs="12">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>ID</b></MudText>
<MudText Typo="Typo.caption">@Context.Value.Id</MudText>
</MudStack>
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Creation Time</b></MudText>
<MudText Typo="Typo.caption">@Context.Details.CreationTime</MudText>
</MudStack>
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Last Modification Time</b></MudText>
<MudText Typo="Typo.caption">@Context.Details.LastModificationTime</MudText>
</MudStack>
</MudItem>
</MudGrid>
</td>
</tr>
}
else
{
<MudTr>
<MudTd colspan="100" Style="border-bottom: 3px solid var(--mud-palette-table-lines);" DataLabel="Row Details">
<MudGrid Spacing="2">
<MudItem xs="12">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>ID</b></MudText>
<MudText Typo="Typo.caption">@Context.Value.Id</MudText>
</MudStack>
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Creation Time</b></MudText>
<MudText Typo="Typo.caption">@Context.Details.CreationTime</MudText>
</MudStack>
</MudItem>
<MudItem xs="4">
<MudStack Spacing="1">
<MudText Typo="Typo.caption"><b>Last Modification Time</b></MudText>
<MudText Typo="Typo.caption">@Context.Details.LastModificationTime</MudText>
</MudStack>
</MudItem>
</MudGrid>
</MudTd>
</MudTr>
}
}

@code
{
[Parameter, EditorRequired]
public ModelWithDetails<TModel, TDetails> Context { get; set; } = default!;

[Parameter]
public bool SimpleTable { get; set; }
}
14 changes: 5 additions & 9 deletions src/DotNetElements.Web.MudBlazor/CrudTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public abstract class CrudTable<TKey, TModel, TDetails, TEditModel, TEditDialog>

protected bool IsLoaded;

private readonly CrudTableOptions options;
private readonly CrudTableOptions<TModel> options;

public CrudTable()
{
options = OnConfiguring();
}

protected abstract CrudTableOptions OnConfiguring();
protected abstract CrudTableOptions<TModel> OnConfiguring();

protected override async Task OnInitializedAsync()
{
Expand Down Expand Up @@ -103,15 +103,11 @@ protected async Task OnEditEntry(ModelWithDetails<TModel, TDetails> context)
}
}

protected async Task OnDeleteEntry(ModelWithDetails<TModel, TDetails> context, string modelName)
protected async Task OnDeleteEntry(ModelWithDetails<TModel, TDetails> context)
{
bool? canDelete = await DialogService.ShowMessageBox(
"Attention",
$"Are you sure you want to delete this {modelName}?",
yesText: "Confirm",
cancelText: "Cancel");
Result canDelete = await DialogService.ShowDeleteDialogAsync($"Delete {options.DeleteEntryLabel}?", options.DeleteEntryValue.Invoke(context.Value), options.DeleteEntryLabel);

if (canDelete is false)
if (canDelete.IsFail)
return;

Result result = await HttpClient.DeleteWithResultAsync(options.BaseEndpointUri, context.Value);
Expand Down
40 changes: 40 additions & 0 deletions src/DotNetElements.Web.MudBlazor/CrudTableActionsCell.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@inherits MudComponentBase

@typeparam TKey where TKey : notnull, IEquatable<TKey>
@typeparam TModel where TModel : IModel<TKey>
@typeparam TDetails where TDetails : ModelDetails

@if (SimpleTable)
{
<td class="py-0 pl-2" style="width: 140px;">
<MudIconButton OnClick="() => OnEditEntry.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Edit" Color="Color.Warning" />
<MudIconButton OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Delete" Color="Color.Error" />
<MudIconButton OnClick="() => OnShowEntryDetails.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Info" Color="Color.Default" />
</td>
}
else
{
<MudTd Class="py-0 pl-2" Style="width: 140px;" DataLabel="Actions">
<MudIconButton OnClick="() => OnEditEntry.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Edit" Color="Color.Warning" />
<MudIconButton OnClick="() => OnDeleteEntry.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Delete" Color="Color.Error" />
<MudIconButton OnClick="() => OnShowEntryDetails.InvokeAsync(Context)" Class="pa-1" Icon="@Icons.Material.Outlined.Info" Color="Color.Default" />
</MudTd>
}

@code
{
[Parameter, EditorRequired]
public ModelWithDetails<TModel, TDetails> Context { get; set; } = default!;

[Parameter, EditorRequired]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnEditEntry { get; set; }

[Parameter, EditorRequired]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnDeleteEntry { get; set; }

[Parameter, EditorRequired]
public EventCallback<ModelWithDetails<TModel, TDetails>> OnShowEntryDetails { get; set; }

[Parameter]
public bool SimpleTable { get; set; }
}
5 changes: 5 additions & 0 deletions src/DotNetElements.Web.MudBlazor/CrudTableLoadingCell.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<tr>
<td colspan="100">
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
</td>
</tr>
21 changes: 21 additions & 0 deletions src/DotNetElements.Web.MudBlazor/CrudTableNewEntryButton.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@inherits MudButton

@{
ChildContent = @<span>Create New</span>;
}

@{
base.BuildRenderTree(__builder);
}

@code
{
public CrudTableNewEntryButton()
{
StartIcon = Icons.Material.Outlined.Add;
Color = Color.Success;
Variant = Variant.Outlined;
Size = Size.Small;
Class = "ml-4";
}
}
5 changes: 4 additions & 1 deletion src/DotNetElements.Web.MudBlazor/CrudTableOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace DotNetElements.Web.MudBlazor;

public class CrudTableOptions
public class CrudTableOptions<TModel>
{
public string BaseEndpointUri { get; private init; }

Expand All @@ -17,6 +17,9 @@ public string GetAllEndpoint
}
}

public required string DeleteEntryLabel { get; set; }
public required Func<TModel, string> DeleteEntryValue { get; set; }

public string GetDetailsEndpoint(string id) => $"{BaseEndpointUri.TrimEnd('/')}/{id}/details";

public DialogOptions EditDialogOptions { get; init; }
Expand Down
18 changes: 18 additions & 0 deletions src/DotNetElements.Web.MudBlazor/CrudTableSearchField.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@inherits MudInput<string>

@{
base.BuildRenderTree(__builder);
}

@code
{
public CrudTableSearchField()
{
Placeholder = "Search";
Clearable = true;
Adornment = Adornment.Start;
AdornmentIcon = Icons.Material.Filled.Search;
IconSize = Size.Medium;
Style = "width: 40%";
}
}
4 changes: 2 additions & 2 deletions src/DotNetElements.Web.MudBlazor/DeleteDialog.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.DeleteForever" Class="mr-3 mb-n1" />
<MudIcon Icon="@Icons.Material.Filled.DeleteForever" Color="Color.Error" Class="mr-3 mb-n1" />
@DialogInstance.Title
</MudText>
</TitleContent>
<DialogContent>
<MudTextField Value="@ItemValue" Label="@ItemLabel" ReadOnly="true" />
<MudTextField Value="@ItemValue" Label="@ItemLabel" FullWidth="true" ReadOnly="true" />
</DialogContent>
<DialogActions>
<MudButton OnClick="OnCancel">Cancel</MudButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public static class DialogeServiceExtensions
{
public static async Task<Result> ShowDeleteDialog(this IDialogService dialogService, string title, string itemValue, string itemLabel)
public static async Task<Result> ShowDeleteDialogAsync(this IDialogService dialogService, string title, string itemValue, string itemLabel)
{
var dialogParameters = new DialogParameters<DeleteDialog>
{
Expand Down
8 changes: 6 additions & 2 deletions src/DotNetElements.Web.MudBlazor/HttpClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace DotNetElements.Web.MudBlazor;

public static class HttpClientExtensions
{
// todo [Performance] Check if we can use custom Json converter for improved performance.
public static async Task<Result<List<ModelWithDetails<TModel, TDetails>>>> GetModelWithDetailsListFromJsonAsync<TModel, TDetails>(this HttpClient httpClient, string? requestUri, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
where TDetails : ModelDetails
{
Expand All @@ -14,9 +15,12 @@ public static async Task<Result<List<ModelWithDetails<TModel, TDetails>>>> GetMo
if (!response.IsSuccessStatusCode)
return Result.Fail($"Get request failed with status: {response.StatusCode} and reason: {response.ReasonPhrase}");

List<ModelWithDetails<TModel, TDetails>>? returnValue = await response.Content.ReadFromJsonAsync<List<ModelWithDetails<TModel, TDetails>>>(cancellationToken);
List<TModel>? returnValue = await response.Content.ReadFromJsonAsync<List<TModel>>(cancellationToken);

return Result.OkIfNotNull(returnValue, $"Failed to convert response content to type ModelWithDetails<{typeof(TModel)}, {typeof(TDetails)}>");
if (returnValue is null)
return Result.Fail($"Failed to convert response content to type {typeof(TModel)}");

return returnValue.Select(model => new ModelWithDetails<TModel, TDetails>(model)).ToList();
}

public static async Task<Result<T>> GetFromJsonWithResultAsync<T>(this HttpClient httpClient, string? requestUri, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
Expand Down
14 changes: 14 additions & 0 deletions src/DotNetElements.Web.MudBlazor/PageHeader.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@using Microsoft.AspNetCore.Components.Sections

<SectionContent SectionName="@SectionName">
<MudText Typo=Typo.h6>@Title</MudText>
</SectionContent>

@code
{
[Parameter, EditorRequired]
public string Title { get; set; } = default!;

[Parameter]
public string SectionName { get; set; } = "page-header";
}
25 changes: 25 additions & 0 deletions src/DotNetElements.Web.MudBlazor/ThemeToggleButton.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@if (DarkModeActive)
{
<MudIconButton Icon="@Icons.Material.Filled.LightMode" Color="Color.Inherit" OnClick="@this.OnToggleDarkMode" Title="To light mode" />
}
else
{
<MudIconButton Icon="@Icons.Material.Filled.DarkMode" Color="Color.Inherit" OnClick="@this.OnToggleDarkMode" Title="To dark mode" />
}

@code
{
[Parameter, EditorRequired]
public bool DarkModeActive { get; set; }

[Parameter]
public EventCallback<bool> DarkModeActiveChanged { get; set; }

private async Task OnToggleDarkMode()
{
DarkModeActive = !DarkModeActive;

if (DarkModeActiveChanged.HasDelegate)
await DarkModeActiveChanged.InvokeAsync(DarkModeActive);
}
}

0 comments on commit 7300da2

Please sign in to comment.