@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore.Query @typeparam TItem @attribute [CascadingTypeParameter(nameof(TItem))] @inject NavigationManager Navigation @ChildContent @foreach (var column in columns) { } @foreach (var item in currentPageItems) { @foreach (var column in columns) { } } @if (FooterContent != null) { @FooterContent }
@if (column.Sortable) { @column.HeaderContent } else { @column.HeaderContent }
@column.CellContent(item)
@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.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" }, }); }