using Cysharp.Text; using HtmlAgilityPack; namespace OliverBooth.Pages.Shared.Partials; /// /// Provides methods for displaying pagination tabs. /// public class PageTabsUtility { private string _urlRoot = string.Empty; /// /// Gets or sets the current page number. /// /// The current page number. public int CurrentPage { get; set; } = 1; /// /// Gets or sets the page count. /// /// The page count. public int PageCount { get; set; } = 1; /// /// Gets or sets the URL root. /// /// The URL root. public string UrlRoot { get => _urlRoot; set => _urlRoot = string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); } /// /// Shows the bound chevrons for the specified bounds type. /// /// The bounds type to display. /// An HTML string containing the elements representing the bound chevrons. public string ShowBounds(BoundsType bounds) { return bounds switch { BoundsType.Lower => ShowLowerBound(), BoundsType.Upper => ShowUpperBound(PageCount), _ => string.Empty }; } /// /// Shows the specified page tab. /// /// The tab to display. /// An HTML string containing the element for the specified page tab. public string ShowTab(int tab) { var document = new HtmlDocument(); HtmlNode listItem = document.CreateElement("li"); HtmlNode pageLink; listItem.AddClass("page-item"); switch (tab) { case 0: // show ... to indicate truncation pageLink = document.CreateElement("span"); pageLink.InnerHtml = "..."; break; case var _ when CurrentPage == tab: listItem.AddClass("active"); listItem.SetAttributeValue("aria-current", "page"); pageLink = document.CreateElement("span"); pageLink.InnerHtml = tab.ToString(); break; default: pageLink = document.CreateElement("a"); pageLink.SetAttributeValue("href", GetLinkForPage(tab)); pageLink.InnerHtml = tab.ToString(); break; } pageLink.AddClass("page-link"); listItem.AppendChild(pageLink); document.DocumentNode.AppendChild(listItem); return document.DocumentNode.InnerHtml; } /// /// Shows the paginated tab window. /// /// An HTML string representing the page tabs. public string ShowTabWindow() { using Utf16ValueStringBuilder builder = ZString.CreateStringBuilder(); int windowLowerBound = Math.Max(CurrentPage - 2, 1); int windowUpperBound = Math.Min(CurrentPage + 2, PageCount); if (windowLowerBound > 2) { // show lower truncation ... builder.AppendLine(ShowTab(0)); } for (int pageIndex = windowLowerBound; pageIndex <= windowUpperBound; pageIndex++) { if (pageIndex == 1 || pageIndex == PageCount) { // don't show bounds, these are explicitly written continue; } builder.AppendLine(ShowTab(pageIndex)); } if (windowUpperBound < PageCount - 1) { // show upper truncation ... builder.AppendLine(ShowTab(0)); } return builder.ToString(); } private string GetLinkForPage(int page) { // page 1 doesn't use /page/n route return page == 1 ? _urlRoot : $"{_urlRoot}/page/{page}"; } private string ShowLowerBound() { if (CurrentPage <= 1) { return string.Empty; } var document = new HtmlDocument(); HtmlNode listItem = document.CreateElement("li"); listItem.AddClass("page-item"); HtmlNode pageLink = document.CreateElement("a"); listItem.AppendChild(pageLink); pageLink.AddClass("page-link"); pageLink.SetAttributeValue("href", UrlRoot); pageLink.InnerHtml = "≪"; document.DocumentNode.AppendChild(listItem); listItem = document.CreateElement("li"); listItem.AddClass("page-item"); pageLink = document.CreateElement("a"); listItem.AppendChild(pageLink); pageLink.AddClass("page-link"); pageLink.InnerHtml = "<"; pageLink.SetAttributeValue("href", GetLinkForPage(CurrentPage - 1)); document.DocumentNode.AppendChild(listItem); return document.DocumentNode.InnerHtml; } private string ShowUpperBound(int pageCount) { if (CurrentPage >= pageCount) { return string.Empty; } var document = new HtmlDocument(); HtmlNode pageLink = document.CreateElement("a"); pageLink.AddClass("page-link"); pageLink.SetAttributeValue("href", GetLinkForPage(CurrentPage + 1)); pageLink.InnerHtml = ">"; HtmlNode listItem = document.CreateElement("li"); listItem.AddClass("page-item"); listItem.AppendChild(pageLink); document.DocumentNode.AppendChild(listItem); pageLink = document.CreateElement("a"); pageLink.AddClass("page-link"); pageLink.SetAttributeValue("href", GetLinkForPage(pageCount)); pageLink.InnerHtml = "≫"; listItem = document.CreateElement("li"); listItem.AddClass("page-item"); listItem.AppendChild(pageLink); document.DocumentNode.AppendChild(listItem); return document.DocumentNode.InnerHtml; } public enum BoundsType { Lower, Upper } }