@* Reusable data table with sorting, filtering, and pagination *@ @typeparam TItem
@if (ShowSearch) {
@if (!string.IsNullOrEmpty(_searchTerm)) { }
@if (FilterContent != null) {
@FilterContent
}
}
@HeaderContent @if (_pagedItems.Count == 0) { } else { @foreach (var item in _pagedItems) { @RowContent(item) } }
@EmptyMessage
@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(); } }