Spaces:
Running
Running
| @* Partial/Pagination.razor - Reusable pagination component *@ | |
| <div class="flex flex-wrap items-center justify-between gap-3 border-t border-slate-100 bg-slate-50/60 px-5 py-3"> | |
| <div class="text-xs text-slate-500"> | |
| @if (TotalCount == 0) | |
| { | |
| <span>No results</span> | |
| } | |
| else | |
| { | |
| var from = (CurrentPage - 1) * PageSize + 1; | |
| var to = Math.Min(CurrentPage * PageSize, TotalCount); | |
| <span>Showing <span class="font-semibold text-slate-700">@from-@to</span> of <span class="font-semibold text-slate-700">@TotalCount</span> results</span> | |
| } | |
| </div> | |
| <div class="flex items-center gap-3"> | |
| <div class="flex items-center gap-2 text-xs text-slate-500"> | |
| <span>Rows per page</span> | |
| <select value="@PageSize" @onchange="HandlePageSizeChange" | |
| class="rounded-lg border border-slate-200 bg-white px-2 py-1 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-600 transition-all"> | |
| <option value="5">5</option> | |
| <option value="10">10</option> | |
| <option value="20">20</option> | |
| <option value="50">50</option> | |
| </select> | |
| </div> | |
| <div class="flex items-center gap-1"> | |
| <button @onclick="() => GoToPage(1)" disabled="@(CurrentPage <= 1)" | |
| class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40" | |
| title="First page"> | |
| <i data-lucide="chevrons-left" class="h-3.5 w-3.5"></i> | |
| </button> | |
| <button @onclick="() => GoToPage(CurrentPage - 1)" disabled="@(CurrentPage <= 1)" | |
| class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40" | |
| title="Previous page"> | |
| <i data-lucide="chevron-left" class="h-3.5 w-3.5"></i> | |
| </button> | |
| @foreach (var p in GetVisiblePages()) | |
| { | |
| if (p == -1) | |
| { | |
| <span class="inline-flex h-8 w-8 items-center justify-center text-xs text-slate-400">...</span> | |
| } | |
| else | |
| { | |
| <button @onclick="() => GoToPage(p)" | |
| class="@GetPageButtonClass(p)"> | |
| @p | |
| </button> | |
| } | |
| } | |
| <button @onclick="() => GoToPage(CurrentPage + 1)" disabled="@(CurrentPage >= TotalPages)" | |
| class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40" | |
| title="Next page"> | |
| <i data-lucide="chevron-right" class="h-3.5 w-3.5"></i> | |
| </button> | |
| <button @onclick="() => GoToPage(TotalPages)" disabled="@(CurrentPage >= TotalPages)" | |
| class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40" | |
| title="Last page"> | |
| <i data-lucide="chevrons-right" class="h-3.5 w-3.5"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| @code { | |
| [Parameter] public int CurrentPage { get; set; } = 1; | |
| [Parameter] public int TotalPages { get; set; } = 1; | |
| [Parameter] public int PageSize { get; set; } = 10; | |
| [Parameter] public int TotalCount { get; set; } | |
| [Parameter] public EventCallback<int> OnPageChanged { get; set; } | |
| [Parameter] public EventCallback<int> OnPageSizeChanged { get; set; } | |
| private string GetPageButtonClass(int p) => p == CurrentPage | |
| ? "inline-flex h-8 w-8 items-center justify-center rounded-lg text-xs font-semibold transition-all bg-violet-600 text-white shadow-sm" | |
| : "inline-flex h-8 w-8 items-center justify-center rounded-lg text-xs font-semibold transition-all text-slate-600 hover:bg-white"; | |
| private async Task GoToPage(int p) | |
| { | |
| if (p < 1 || p > TotalPages || p == CurrentPage) return; | |
| await OnPageChanged.InvokeAsync(p); | |
| } | |
| private async Task HandlePageSizeChange(ChangeEventArgs e) | |
| { | |
| if (int.TryParse(e.Value?.ToString(), out var size) && size != PageSize) | |
| { | |
| await OnPageSizeChanged.InvokeAsync(size); | |
| } | |
| } | |
| private IEnumerable<int> GetVisiblePages() | |
| { | |
| if (TotalPages <= 7) | |
| { | |
| return Enumerable.Range(1, TotalPages); | |
| } | |
| var pages = new List<int> { 1 }; | |
| int start = Math.Max(2, CurrentPage - 1); | |
| int end = Math.Min(TotalPages - 1, CurrentPage + 1); | |
| if (start > 2) pages.Add(-1); | |
| for (int i = start; i <= end; i++) pages.Add(i); | |
| if (end < TotalPages - 1) pages.Add(-1); | |
| pages.Add(TotalPages); | |
| return pages; | |
| } | |
| } | |