feat: Add reusable Pager component for list pagination
Shared Blazor component with page windowing, ellipsis for large page counts, and "Showing X-Y of Z" summary text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
109
CutList.Web/Components/Shared/Pager.razor
Normal file
109
CutList.Web/Components/Shared/Pager.razor
Normal file
@@ -0,0 +1,109 @@
|
||||
@if (TotalCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@{
|
||||
var totalPages = (int)Math.Ceiling((double)TotalCount / PageSize);
|
||||
var start = (CurrentPage - 1) * PageSize + 1;
|
||||
var end = Math.Min(CurrentPage * PageSize, TotalCount);
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-3">
|
||||
<small class="text-muted">Showing @start–@end of @TotalCount</small>
|
||||
|
||||
@if (totalPages > 1)
|
||||
{
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
<li class="page-item @(CurrentPage == 1 ? "disabled" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(CurrentPage - 1)" disabled="@(CurrentPage == 1)">Previous</button>
|
||||
</li>
|
||||
|
||||
@foreach (var page in GetPageWindow(totalPages))
|
||||
{
|
||||
@if (page == -1)
|
||||
{
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">…</span>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item @(page == CurrentPage ? "active" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(page)">@(page)</button>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
<li class="page-item @(CurrentPage == totalPages ? "disabled" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(CurrentPage + 1)" disabled="@(CurrentPage == totalPages)">Next</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter, EditorRequired]
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int PageSize { get; set; } = 25;
|
||||
|
||||
[Parameter]
|
||||
public int CurrentPage { get; set; } = 1;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<int> CurrentPageChanged { get; set; }
|
||||
|
||||
private async Task SetPage(int page)
|
||||
{
|
||||
if (page < 1 || page > (int)Math.Ceiling((double)TotalCount / PageSize))
|
||||
return;
|
||||
if (page == CurrentPage)
|
||||
return;
|
||||
|
||||
CurrentPage = page;
|
||||
await CurrentPageChanged.InvokeAsync(page);
|
||||
}
|
||||
|
||||
private IEnumerable<int> GetPageWindow(int totalPages)
|
||||
{
|
||||
const int maxVisible = 7;
|
||||
|
||||
if (totalPages <= maxVisible)
|
||||
{
|
||||
for (var i = 1; i <= totalPages; i++)
|
||||
yield return i;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Always show first page
|
||||
yield return 1;
|
||||
|
||||
var windowStart = Math.Max(2, CurrentPage - 2);
|
||||
var windowEnd = Math.Min(totalPages - 1, CurrentPage + 2);
|
||||
|
||||
// Adjust window to show 5 middle pages when possible
|
||||
if (windowEnd - windowStart < 4)
|
||||
{
|
||||
if (windowStart == 2)
|
||||
windowEnd = Math.Min(totalPages - 1, windowStart + 4);
|
||||
else
|
||||
windowStart = Math.Max(2, windowEnd - 4);
|
||||
}
|
||||
|
||||
if (windowStart > 2)
|
||||
yield return -1; // ellipsis
|
||||
|
||||
for (var i = windowStart; i <= windowEnd; i++)
|
||||
yield return i;
|
||||
|
||||
if (windowEnd < totalPages - 1)
|
||||
yield return -1; // ellipsis
|
||||
|
||||
// Always show last page
|
||||
yield return totalPages;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user