@using Microsoft.EntityFrameworkCore
@using Microsoft.EntityFrameworkCore.Query
@typeparam TItem
@attribute [CascadingTypeParameter(nameof(TItem))]
@inject NavigationManager Navigation
@ChildContent
@foreach (var column in columns)
{
@if (column.Sortable)
{
@column.HeaderContent
}
else
{
@column.HeaderContent
}
|
}
@foreach (var item in currentPageItems)
{
@foreach (var column in columns)
{
@column.CellContent(item)
|
}
}
@if (FooterContent != null)
{
@FooterContent
}
@code {
private readonly List> columns = [];
private TItem[] currentPageItems = [];
private IQueryable? lastAssignedItems;
private int? lastLoadedPaginationStateHash;
[Parameter, EditorRequired]
public required IQueryable Items { get; set; }
[Parameter]
public PaginationState? Pagination { get; set; }
[Parameter]
public string? HeaderClass { get; set; }
[Parameter]
public string? CellClass { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public RenderFragment? FooterContent { get; set; }
[SupplyParameterFromQuery(Name = "page")]
private int CurrentPage { get; set; }
[SupplyParameterFromQuery(Name = "sort")]
private string? SortKey { get; set; }
[SupplyParameterFromQuery(Name = "desc")]
private bool SortDescending { get; set; }
protected override async Task OnParametersSetAsync()
{
var dataSourceHasChanged = Items != lastAssignedItems;
if (dataSourceHasChanged)
{
lastAssignedItems = Items;
}
if (Pagination != null)
{
Pagination.CurrentPage = CurrentPage;
}
if (dataSourceHasChanged || Pagination?.GetHashCode() != lastLoadedPaginationStateHash)
{
await LoadDataAsync();
}
}
internal void AddColumn(TableColumn column)
{
columns.Add(column);
StateHasChanged();
}
private async Task LoadDataAsync()
{
await LoadDataCoreAsync();
lastLoadedPaginationStateHash = Pagination?.GetHashCode();
}
private async Task LoadDataCoreAsync()
{
if (Pagination?.CurrentPage < 1)
{
Pagination.CurrentPage = 1;
NavigateToCurrentPage();
return;
}
var totalCount = Items.Provider is IAsyncQueryProvider
? await Items.CountAsync()
: Items.Count();
var itemsQuery = Items;
if (SortKey != null &&
columns
.Select(column => column.SortBy)
.SingleOrDefault(sortBy => sortBy?.Key == SortKey) is DataSort sortBy)
{
itemsQuery = sortBy.Apply(itemsQuery, SortDescending);
}
if (Pagination != null)
{
Pagination.TotalItemCount = totalCount;
if (Pagination.LastPage < Pagination.CurrentPage)
{
Pagination.CurrentPage = Pagination.LastPage;
NavigateToCurrentPage();
return;
}
itemsQuery = itemsQuery
.Skip(Pagination.Offset)
.Take(Pagination.PageSize);
}
currentPageItems = Items.Provider is IAsyncQueryProvider
? await itemsQuery.ToArrayAsync()
: itemsQuery.ToArray();
if (Pagination != null)
{
Pagination.ItemCount = currentPageItems.Length;
}
StateHasChanged();
}
private void NavigateToCurrentPage()
{
Navigation.NavigateTo(Navigation.GetUriWithQueryParameter("page", Pagination!.CurrentPage));
}
private string? GetAriaSortValue(TableColumn column)
=> column.Sortable && SortKey == column.SortBy!.Key
? (SortDescending ? "descending" : "ascending")
: null;
private string GetHeaderClass(TableColumn column)
{
string?[] classes = [
"table__header",
column.Align switch
{
Align.Center => "table__header--align-center",
Align.End => "table__header--align-end",
_ => null,
},
column.Sortable ? "table__header--sortable" : null,
HeaderClass,
];
return string.Join(' ', classes.Where(c => c != null));
}
private string GetCellClass(TableColumn column)
{
string?[] classes = [
"table__cell",
column.Align switch
{
Align.Center => "table__cell--align-center",
Align.End => "table__cell--align-end",
_ => null,
},
CellClass,
];
return string.Join(' ', classes.Where(c => c != null));
}
private string GetUriForColumnSort(TableColumn column)
=> Navigation.GetUriWithQueryParameters(new Dictionary
{
{ "page", 1 },
{ "sort", SortKey == column.SortBy!.Key && SortDescending ? null : column.SortBy.Key},
{ "desc", SortKey != column.SortBy!.Key || SortDescending ? null : "true" },
});
}