You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Components/Components/Pagination/Pagination.razor.cs

325 lines
9.2 KiB

// Copyright (c) MudBlazor 2021
// MudBlazor licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class Pagination : UIComponent
{
#region Css Classes
private string Classname =>
new CssBuilder("mud-pagination")
.AddClass($"mud-pagination-{Variant.ToDescriptionString()}")
.AddClass($"mud-pagination-{Size.ToDescriptionString()}")
.AddClass("mud-pagination-disable-elevation", DisableElevation)
.AddClass("mud-pagination-rtl", RightToLeft)
.AddClass(Class)
.Build();
private string ItemClassname =>
new CssBuilder("mud-pagination-item")
.AddClass("mud-pagination-item-rectangular", Rectangular)
.Build();
private string SelectedItemClassname =>
new CssBuilder(ItemClassname)
.AddClass("mud-pagination-item-selected")
.Build();
#endregion
#region Parameter
private int _count = 1;
/// <summary>
/// The number of pages.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public int Count
{
get => _count;
set
{
_count = Math.Max(1, value);
Selected = Math.Min(Selected, _count);
}
}
private int _boundaryCount = 2;
/// <summary>
/// The number of items at the start and end of the pagination.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public int BoundaryCount
{
get => _boundaryCount;
set
{
_boundaryCount = Math.Max(1, value);
}
}
private int _middleCount = 3;
/// <summary>
/// The number of items in the middle of the pagination.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public int MiddleCount
{
get => _middleCount;
set
{
_middleCount = Math.Max(1, value);
}
}
private bool _selectedFirstSet;
private int _selected = 1;
/// <summary>
/// The selected page number.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public int Selected
{
get => _selected;
set
{
if (_selected == value)
return;
//this is required because _selected will stay 1 when Count is not yet set
if (!_selectedFirstSet)
{
_selected = value;
_selectedFirstSet = true;
}
else
_selected = Math.Max(1, Math.Min(value, Count));
SelectedChanged.InvokeAsync(_selected);
}
}
/// <summary>
/// The variant to use.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// The color of the component. It supports the theme colors.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Primary;
/// <summary>
/// If true, the pagination buttons are displayed rectangular.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public bool Rectangular { get; set; }
/// <summary>
/// The size of the component..
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public Size Size { get; set; } = Size.Medium;
/// <summary>
/// If true, no drop-shadow will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public bool DisableElevation { get; set; }
/// <summary>
/// If true, the pagination will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// If true, the navigate-to-first-page button is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public bool ShowFirstButton { get; set; }
/// <summary>
/// If true, the navigate-to-last-page button is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public bool ShowLastButton { get; set; }
/// <summary>
/// If true, the navigate-to-previous-page button is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public bool ShowPreviousButton { get; set; } = true;
/// <summary>
/// If true, the navigate-to-next-page button is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Behavior)]
public bool ShowNextButton { get; set; } = true;
/// <summary>
/// Invokes the callback when a control button is clicked.
/// </summary>
[Parameter]
public EventCallback<Page> ControlButtonClicked { get; set; }
/// <summary>
/// Invokes the callback when selected page changes.
/// </summary>
[Parameter]
public EventCallback<int> SelectedChanged { get; set; }
/// <summary>
/// Custom first icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public string FirstIcon { get; set; } = Icons.Material.Filled.FirstPage;
/// <summary>
/// Custom before icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public string BeforeIcon { get; set; } = Icons.Material.Filled.NavigateBefore;
/// <summary>
/// Custom next icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public string NextIcon { get; set; } = Icons.Material.Filled.NavigateNext;
/// <summary>
/// Custom last icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Pagination.Appearance)]
public string LastIcon { get; set; } = Icons.Material.Filled.LastPage;
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
#endregion
#region Methods
/*generates an array representing the pagination numbers, e.g. for Count==11, MiddleCount==3, BoundaryCount==1,
Selected==6 the output will be the int array [1, 2, -1, 5, 6, 7, -1, 10, 11]
-1 is displayed as "..." in the ui*/
private IEnumerable<int> GeneratePagination()
{
//return array {1, ..., Count} if Count is small
if (Count <= 4 || Count <= 2 * BoundaryCount + MiddleCount + 2)
return Enumerable.Range(1, Count).ToArray();
var length = 2 * BoundaryCount + MiddleCount + 2;
var pages = new int[length];
//set start boundary items, e.g. if BoundaryCount == 3 => [1, 2, 3, ...]
for (var i = 0; i < BoundaryCount; i++)
{
pages[i] = i + 1;
}
//set end boundary items, e.g. if BoundaryCount == 3 and Count == 11 => [..., 9, 10, 11]
for (var i = 0; i < BoundaryCount; i++)
{
pages[length - i - 1] = Count - i;
}
//calculate start value for middle items
var startValue = 0;
if (Selected <= BoundaryCount + MiddleCount / 2 + 1)
startValue = BoundaryCount + 2;
else if (Selected >= Count - BoundaryCount - MiddleCount / 2)
startValue = Count - BoundaryCount - MiddleCount;
else
startValue = Selected - MiddleCount / 2;
//set middle items, e.g. if MiddleCount == 3 and Selected == 5 and Count == 11 => [..., 4, 5, 6, ...]
for (var i = 0; i < MiddleCount; i++)
{
pages[BoundaryCount + 1 + i] = startValue + i;
}
//set start delimiter "..." when selected page is far enough to the end, dots are represented as -1
pages[BoundaryCount] = (BoundaryCount + MiddleCount / 2 + 1 < Selected) ? -1 : BoundaryCount + 1;
//set end delimiter "..." when selected page is far enough to the start, dots are represented as -1
pages[length - BoundaryCount - 1] = (Count - BoundaryCount - MiddleCount / 2 > Selected) ? -1 : Count - BoundaryCount;
//remove ellipsis if difference is small enough, e.g convert [..., 5 , -1 , 7, ...] to [..., 5, 6, 7, ...]
for (var i = 0; i < length - 2; i++)
{
if (pages[i] + 2 == pages[i + 2])
pages[i + 1] = pages[i] + 1;
}
return pages;
}
//triggered when the user clicks on a control button, e.g. the navigate-to-next-page-button
private void OnClickControlButton(Page page)
{
ControlButtonClicked.InvokeAsync(page);
NavigateTo(page);
}
//Last line cannot be tested because Page enum has 4 items
/// <summary>
/// Navigates to the specified page.
/// </summary>
/// <param name="page">The target page. page=Page.Next navigates to the next page.</param>
[ExcludeFromCodeCoverage]
public void NavigateTo(Page page)
{
Selected = page switch
{
Page.First => 1,
Page.Last => Math.Max(1, Count),
Page.Next => Math.Min(Selected + 1, Count),
Page.Previous => Math.Max(1, Selected - 1),
_ => Selected
};
}
/// <summary>
/// Navigates to the specified page.
/// </summary>
/// <param name="pageIndex"></param>The target page. pageIndex=2 navigates to the 3. page.
public void NavigateTo(int pageIndex)
{
Selected = pageIndex + 1;
}
#endregion
}