@* Reusable data table with sorting, filtering, and pagination *@
@typeparam TItem
@if (ShowSearch)
{
@if (FilterContent != null)
{
@FilterContent
}
}
@HeaderContent
@if (_pagedItems.Count == 0)
{
| @EmptyMessage |
}
else
{
@foreach (var item in _pagedItems)
{
@RowContent(item)
}
}
@if (_totalPages > 1)
{
}
Showing @((_currentPage - 1) * PageSize + 1)–@Math.Min(_currentPage * PageSize, _filteredItems.Count) of @_filteredItems.Count items
@code {
private string _searchTerm = string.Empty;
private int _currentPage = 1;
private List _filteredItems = new();
private List _pagedItems = new();
private int _totalPages;
[Parameter, EditorRequired] public IReadOnlyList Items { get; set; } = [];
[Parameter, EditorRequired] public RenderFragment HeaderContent { get; set; } = default!;
[Parameter, EditorRequired] public RenderFragment RowContent { get; set; } = default!;
[Parameter] public RenderFragment? FilterContent { get; set; }
[Parameter] public int PageSize { get; set; } = 25;
[Parameter] public bool ShowSearch { get; set; } = true;
[Parameter] public string EmptyMessage { get; set; } = "No items found.";
[Parameter] public Func? SearchFilter { get; set; }
protected override void OnParametersSet()
{
ApplyFilter();
}
private void ApplyFilter()
{
if (!string.IsNullOrWhiteSpace(_searchTerm) && SearchFilter != null)
{
_filteredItems = Items.Where(i => SearchFilter(i, _searchTerm)).ToList();
}
else
{
_filteredItems = Items.ToList();
}
_totalPages = Math.Max(1, (int)Math.Ceiling(_filteredItems.Count / (double)PageSize));
if (_currentPage > _totalPages) _currentPage = 1;
UpdatePage();
}
private void GoToPage(int page)
{
if (page < 1 || page > _totalPages) return;
_currentPage = page;
UpdatePage();
}
private void ClearSearch()
{
_searchTerm = string.Empty;
ApplyFilter();
}
private void UpdatePage()
{
_pagedItems = _filteredItems
.Skip((_currentPage - 1) * PageSize)
.Take(PageSize)
.ToList();
}
public void Refresh()
{
ApplyFilter();
StateHasChanged();
}
}