Skip to content

Instantly share code, notes, and snippets.

@jimjam88
Last active July 18, 2024 19:33
Show Gist options
  • Save jimjam88/bdf787caf6eee1fd202c2ee24b9f7e0f to your computer and use it in GitHub Desktop.
Save jimjam88/bdf787caf6eee1fd202c2ee24b9f7e0f to your computer and use it in GitHub Desktop.
TypeScript Pagination Values Calculator
import range from "lodash.range";
type PageRangeArgs = {
size?: number,
last: number,
current: number,
}
type PageRangeReturnValue = number[];
type CalculatePaginationArgs = {
total: number,
limit: number,
offset: number,
}
type CalculatePaginationReturnValue = {
total: number,
current: number,
previous: number | undefined,
next: number | undefined,
last: number,
range: PageRangeReturnValue,
}
type GetOffsetArgs = {
limit: number,
page?: number,
}
/**
* Get a balanced range of page numbers.
*
* @note lodash.range is zero based, hence the +1's as the last argument.
*
* @param last
* @param current
* @param size
*/
const getPageRange = ({last, current, size = 7}: PageRangeArgs): PageRangeReturnValue => {
if (size % 2 === 0 || size < 3) {
throw new Error('Invalid range size - Must be odd and greater than 3.');
}
const middle = Math.ceil(size / 2);
// If there are less pages than the requested range size, then only generate
// the available page numbers
if (last < size) {
return range(1, last + 1);
}
// If the current page number plus the right hand offset exceeds the last page,
// then adjust the range so that the last page is furthest to the right of the range
if ((current + middle) > last) {
return range(last - (size - 1), last + 1);
}
// If the current page is less than the middle value then start from page 1 up to size.
if (current <= middle) {
return range(1, size + 1);
}
// Else - we aren't close to any boundaries. Balance the range using the current page
// as the centre.
return range(current - (middle - 1), current + middle);
}
/**
* Calculate pagination data given the total number of pages, a limit and an offset.
*
* @example Start of the range
* console.log(calculatePagination({ total: 200, limit: 25, offset: 0 }));
*
* Output:
* {
* "current": 1,
* "next": 2,
* "previous": undefined,
* "last": 8,
* "range": [
* 1,
* 2,
* 3,
* 4,
* 5,
* 6,
* 7
* ]
* }
*
* @example Middle of the range
* console.log(calculatePagination({ total: 1000, limit: 25, offset: 390 }));
*
* Output:
* {
* "current": 16,
* "previous": 15,
* "next": 17,
* "last": 40,
* "range": [
* 13,
* 14,
* 15,
* 16,
* 17,
* 18,
* 19
* ]
* }
*
* @example End of the range
* console.log(calculatePagination({ total: 400, limit: 25, offset: 390 }));
*
* Output:
* {
* "current": 16,
* "previous": 15,
* "next": undefined,
* "last": 16,
* "range": [
* 10,
* 11,
* 12,
* 13,
* 14,
* 15,
* 16
* ]
* }
*
* @param total
* @param limit
* @param offset
*/
export const calculatePagination = ({total, limit, offset}: CalculatePaginationArgs): CalculatePaginationReturnValue => {
const last = total <= limit ? 1 : Math.ceil((total + 1) / limit);
const current = Math.floor((offset + limit) / limit);
const isCurrentPageTheLastPage = current === last;
const isCurrentPageTheFirstPage = current <= 1;
return {
total,
current,
previous: isCurrentPageTheFirstPage ? undefined : current - 1,
next: isCurrentPageTheLastPage ? undefined : current + 1,
last: (isCurrentPageTheLastPage ? current : last) || 1,
range: getPageRange({last, current}),
};
};
/**
* Get a offset value given a limit and page number.
*
* @param limit
* @param page
*/
export const getOffsetFromLimitAndPageNumber = ({limit, page}: GetOffsetArgs): number => {
if (!page || page <= 1) {
return 0;
}
return (page - 1) * (limit + 1);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment