mirror of
https://github.com/datahub-project/datahub.git
synced 2025-07-31 21:36:08 +00:00
192 lines
4.5 KiB
TypeScript
192 lines
4.5 KiB
TypeScript
import Component from '@ember/component';
|
|
import { computed } from '@ember/object';
|
|
import { tagName } from '@ember-decorators/component';
|
|
import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link';
|
|
|
|
/**
|
|
* Query params with pages for routes
|
|
*/
|
|
interface IQueryParamsWithPages {
|
|
page: number;
|
|
}
|
|
|
|
/**
|
|
* Page structure to render pages
|
|
*/
|
|
interface IPage<T = unknown, P extends IQueryParamsWithPages = { page: number }> {
|
|
pageNumber: number;
|
|
isCurrent: boolean;
|
|
isSeparator: boolean;
|
|
link: IDynamicLinkNode<T, string, P>;
|
|
}
|
|
|
|
/**
|
|
* Type alias to improve readability
|
|
*/
|
|
type IPageWithParams<T, P extends object> = IPage<T, P & IQueryParamsWithPages>;
|
|
|
|
/**
|
|
* Type alias: Object with pages
|
|
*/
|
|
type WithPages<Z extends object> = Z & IQueryParamsWithPages;
|
|
|
|
/**
|
|
* Util to add page param to query params in a link
|
|
* @param linkTo
|
|
* @param page
|
|
*/
|
|
const addPageToLink = <T, P, Z extends object>(
|
|
linkTo: IDynamicLinkNode<T, P, Z>,
|
|
page: number
|
|
): IDynamicLinkNode<T, P, WithPages<Z>> => {
|
|
return {
|
|
...linkTo,
|
|
queryParams: {
|
|
...((linkTo.queryParams as Z) || {}),
|
|
page
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Nacho Pagination component:
|
|
*
|
|
* it will render pagination component. Parameters:
|
|
*
|
|
* currentPage: The page that is current
|
|
* totalPages: Number of pages to show
|
|
* linkTo: dynamic link type of link (https://github.com/asross/dynamic-link). This library will add
|
|
* page parameter automatically
|
|
*
|
|
* This component should be moved to its own addon
|
|
*/
|
|
@tagName('')
|
|
export default class NachoPagination<T, P extends object> extends Component {
|
|
/**
|
|
* Current page to render
|
|
*/
|
|
currentPage: number;
|
|
|
|
/**
|
|
* Number of pages that are available
|
|
*/
|
|
totalPages: number;
|
|
|
|
/**
|
|
* Number of pages to show before and after current page before we show ellipsis '...'.
|
|
*/
|
|
threshold: number = 3;
|
|
|
|
/**
|
|
* Dynamic link to generate page links
|
|
*/
|
|
linkTo: IDynamicLinkNode<T, string, P>;
|
|
|
|
/**
|
|
* Previous page number
|
|
*/
|
|
@computed('currentPage')
|
|
get previousPage(): number {
|
|
const currentPage = this.currentPage;
|
|
if (currentPage <= 1) {
|
|
return currentPage;
|
|
} else {
|
|
return currentPage - 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Next page number
|
|
*/
|
|
@computed('currentPage')
|
|
get nextPage(): number {
|
|
const currentPage = this.currentPage;
|
|
const totalPages = this.totalPages;
|
|
if (currentPage >= totalPages) {
|
|
return totalPages;
|
|
} else {
|
|
return currentPage + 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If current page is first page
|
|
*/
|
|
@computed('currentPage')
|
|
get first(): boolean {
|
|
const currentPage = this.currentPage;
|
|
if (currentPage <= 1) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If current page is last page
|
|
*/
|
|
@computed('currentPage')
|
|
get last(): boolean {
|
|
const currentPage = this.currentPage;
|
|
const totalPages = this.totalPages;
|
|
if (currentPage >= totalPages) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate previous page link
|
|
*/
|
|
@computed('linkTo', 'previousPage')
|
|
get prevLink(): IDynamicLinkNode<T, string, WithPages<P>> {
|
|
return addPageToLink(this.linkTo, this.previousPage);
|
|
}
|
|
|
|
/**
|
|
* Generate next page link
|
|
*/
|
|
@computed('linkTo', 'nextPage')
|
|
get nextLink(): IDynamicLinkNode<T, string, WithPages<P>> {
|
|
return addPageToLink(this.linkTo, this.nextPage);
|
|
}
|
|
|
|
/**
|
|
* Will generate the array of pages to show this:
|
|
* 1 ... 4 5 6 ... 9
|
|
*/
|
|
@computed('currentPage', 'totalPages', 'linkTo')
|
|
get pages(): Array<IPageWithParams<T, P>> {
|
|
const { currentPage, totalPages, linkTo, threshold } = this;
|
|
const pages: Array<IPageWithParams<T, P>> = [];
|
|
const hasInitialSepartor = currentPage - threshold > 2;
|
|
const hasLastSeparator = currentPage + threshold < totalPages - 1;
|
|
const startLoop = hasInitialSepartor ? currentPage - threshold : 2;
|
|
const endLoop = hasLastSeparator ? currentPage + threshold : totalPages - 1;
|
|
|
|
const addPage = ({ pageNumber = -1, isSeparator = false }: Partial<IPage>): void => {
|
|
pages.push({
|
|
pageNumber,
|
|
isSeparator,
|
|
isCurrent: pageNumber === currentPage,
|
|
link: addPageToLink(linkTo, pageNumber)
|
|
});
|
|
};
|
|
|
|
// 1
|
|
addPage({ pageNumber: 1 });
|
|
// ...
|
|
hasInitialSepartor && addPage({ isSeparator: true });
|
|
// 4, 5, 6
|
|
for (let i = startLoop; i <= endLoop; i += 1) {
|
|
addPage({ pageNumber: i });
|
|
}
|
|
// ...
|
|
hasLastSeparator && addPage({ isSeparator: true });
|
|
// 8
|
|
totalPages > 1 && addPage({ pageNumber: totalPages });
|
|
return pages;
|
|
}
|
|
}
|