Skip to content

Commit dce269b

Browse files
fjallemarkclaude
andcommitted
Release 1.3.0. Added PaginationExtensions.
Adds PaginationExtensions for splitting sequences into pages for printing, with XML documentation and README coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 884a300 commit dce269b

3 files changed

Lines changed: 92 additions & 4 deletions

File tree

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<Company>Tellurian Interactive</Company>
66
<Copyright>© Stefan Fjällemark 2023-2026</Copyright>
77
<Authors>Stefan Fjällemark</Authors>
8-
<Version>1.2.4</Version>
9-
<AssemblyVersion>1.2.4.0</AssemblyVersion>
8+
<Version>1.3.0</Version>
9+
<AssemblyVersion>1.3.0.0</AssemblyVersion>
1010
<NeutralLanguage>en-GB</NeutralLanguage>
1111
<Platforms>AnyCPU</Platforms>
1212
<ImplicitUsings>true</ImplicitUsings>
@@ -15,8 +15,8 @@
1515
<PropertyGroup>
1616
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1717
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
18-
<PackageTags>extensions string IDataRecord DataSet Linq</PackageTags>
19-
<PackageReleaseNotes></PackageReleaseNotes>
18+
<PackageTags>extensions string IDataRecord DataSet Linq pagination</PackageTags>
19+
<PackageReleaseNotes>Added PaginationExtensions for splitting sequences into pages for printing.</PackageReleaseNotes>
2020
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
2121
<IncludeSymbols>true</IncludeSymbols>
2222
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ Extensions on `IEnumerable<T>`:
7474
- `IndexOf(Func<T, bool>)` - Returns zero-based index of first element matching the predicate, or -1 if not found.
7575
- `TryGetFirstValue(Func<T,bool>, out T?)` - Try-get pattern for finding first matching item.
7676

77+
## Pagination Extensions
78+
Namespace `Tellurian.Utilities.Printing`
79+
80+
Extensions on `IEnumerable<T>` for splitting a sequence into pages for printing:
81+
- `TotalPages(int itemsPerPage)` - Returns the total number of pages needed to display all items, or 0 if the sequence is null or empty.
82+
- `Page(int itemPerPage, int pageNumber)` - Returns the items on the specified one-based page, or an empty sequence if the page number is out of range.
83+
- `ItemsPerPage(int itemsPerPage)` - Splits the sequence into pages, each containing at most the specified number of items.
84+
- `Pages(Func<T,double> height, int maxHeight, Func<T,T,bool>? pagebreak, double initialHeight = 0.0)` - Splits the sequence into pages based on accumulated item height, with an optional rule to force page breaks.
85+
7786
## Date and Time Extensions
7887
Namespace `Tellurian.Utilities`
7988

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace Tellurian.Utilities.Printing;
2+
3+
/// <summary>
4+
/// Provides extension methods for splitting a sequence of items into pages for printing.
5+
/// </summary>
6+
public static class PaginationExtensions
7+
{
8+
extension<T>(IEnumerable<T> items)
9+
{
10+
/// <summary>
11+
/// Calculates the total number of pages required to display all items, given a fixed number of items per page.
12+
/// </summary>
13+
/// <param name="itemsPerPage">The maximum number of items that fit on a single page.</param>
14+
/// <returns>The total number of pages, or 0 if the sequence is null or empty.</returns>
15+
public int TotalPages(int itemsPerPage)
16+
{
17+
if (items is null) return 0;
18+
var count = items.Count();
19+
if (count == 0) return 0;
20+
return (count + itemsPerPage - 1) / itemsPerPage;
21+
}
22+
23+
/// <summary>
24+
/// Returns the items belonging to the specified page.
25+
/// </summary>
26+
/// <param name="itemPerPage">The maximum number of items that fit on a single page.</param>
27+
/// <param name="pageNumber">The one-based page number to return.</param>
28+
/// <returns>The items on the requested page, or an empty sequence if <paramref name="pageNumber"/> is out of range.</returns>
29+
public IEnumerable<T> Page(int itemPerPage, int pageNumber) =>
30+
pageNumber < 1 || pageNumber > items.TotalPages(itemPerPage) ? [] :
31+
items.Skip((pageNumber - 1) * itemPerPage).Take(itemPerPage);
32+
33+
/// <summary>
34+
/// Splits the sequence into pages, each containing at most the specified number of items.
35+
/// </summary>
36+
/// <param name="itemsPerPage">The maximum number of items that fit on a single page.</param>
37+
/// <returns>A sequence of pages, where each page is a sequence of items.</returns>
38+
public IEnumerable<IEnumerable<T>> ItemsPerPage(int itemsPerPage)
39+
{
40+
var totalPages = items.TotalPages(itemsPerPage);
41+
return Enumerable.Range(1, totalPages).Select(page => items.Page(itemsPerPage, page));
42+
}
43+
44+
/// <summary>
45+
/// Splits the sequence into pages based on the accumulated height of the items rather than a fixed item count.
46+
/// Items are added to the current page until adding the next item would exceed <paramref name="maxHeight"/>,
47+
/// or until an optional page break rule forces a new page.
48+
/// </summary>
49+
/// <param name="height">A function that returns the height of an individual item.</param>
50+
/// <param name="maxHeight">The maximum total height allowed on a single page.</param>
51+
/// <param name="pagebreak">An optional function that, given the previous and current item, returns true to force a page break before the current item; or null to apply no such rule.</param>
52+
/// <param name="initialHeight">The height already consumed at the top of each page, for example by a header. Defaults to 0.</param>
53+
/// <returns>A sequence of pages, where each page is a sequence of items whose combined height fits within <paramref name="maxHeight"/>.</returns>
54+
public IEnumerable<IEnumerable<T>> Pages(Func<T, double> height, int maxHeight, Func<T, T, bool>? pagebreak, double initialHeight = 0.0)
55+
{
56+
List<List<T>> pages = [];
57+
List<T> page = [];
58+
var currentHeight = initialHeight;
59+
foreach (var item in items)
60+
{
61+
var itemHeight = height(item);
62+
var nextHeight = currentHeight + itemHeight;
63+
if (nextHeight > maxHeight || (pagebreak is not null && page.Count != 0 && pagebreak(page.Last(), item)))
64+
{
65+
pages.Add(page);
66+
page = [];
67+
currentHeight = initialHeight + itemHeight;
68+
}
69+
else
70+
{
71+
currentHeight += itemHeight;
72+
}
73+
page.Add(item);
74+
}
75+
if (page.Count > 0) { pages.Add(page); }
76+
return pages;
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)