@visulima/pagination 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ ## @visulima/pagination 1.0.0 (2022-11-15)
2
+
3
+
4
+ ### Features
5
+
6
+ * added new packages for faster api creation ([#14](https://github.com/visulima/visulima/issues/14)) ([eb64fcf](https://github.com/visulima/visulima/commit/eb64fcf33f2a75ea48262ad6e71f80e159a93972))
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 visulima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ <div align="center">
2
+ <h3>Visulima pagination</h3>
3
+ <p>
4
+ Visulima pagination is ... .
5
+
6
+ </p>
7
+ </div>
8
+
9
+ <br />
10
+
11
+ <div align="center">
12
+
13
+ [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ <div align="center">
20
+ <p>
21
+ <sup>
22
+ Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
23
+ </sup>
24
+ </p>
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## Features
30
+
31
+ ## Installation
32
+
33
+ ```sh
34
+ npm install @visulima/pagination
35
+ ```
36
+
37
+ ```sh
38
+ yarn add @visulima/pagination
39
+ ```
40
+
41
+ ```sh
42
+ pnpm add @visulima/pagination
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```ts
48
+ import { createPagination } from "@visulima/pagination";
49
+
50
+ ```
51
+
52
+ ## Supported Node.js Versions
53
+
54
+ Libraries in this ecosystem make the best effort to track
55
+ [Node.js’ release schedule](https://github.com/nodejs/release#release-schedule). Here’s [a
56
+ post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a).
57
+
58
+ ## Contributing
59
+
60
+ If you would like to help take a look at the [list of issues](https://github.com/visulima/visulima/issues) and check our [Contributing](.github/CONTRIBUTING.md) guild.
61
+
62
+ > **Note:** please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
63
+
64
+ ## Credits
65
+
66
+ - [Daniel Bannert](https://github.com/prisis)
67
+ - [All Contributors](https://github.com/visulima/visulima/graphs/contributors)
68
+
69
+ ## License
70
+
71
+ The visulima pagination is open-sourced software licensed under the [MIT][license-url]
72
+
73
+ [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
74
+ [typescript-url]: "typescript"
75
+ [license-image]: https://img.shields.io/npm/l/@visulima/pagination?color=blueviolet&style=for-the-badge
76
+ [license-url]: LICENSE.md "license"
77
+ [npm-image]: https://img.shields.io/npm/v/@visulima/pagination/alpha.svg?style=for-the-badge&logo=npm
78
+ [npm-url]: https://www.npmjs.com/package/@visulima/pagination/v/alpha "npm"
79
+
@@ -0,0 +1,94 @@
1
+ type PaginationResult<Result> = { meta: PaginationMeta; data: Result[] };
2
+ type PaginationMeta = {
3
+ total: number;
4
+ perPage: number;
5
+ page: number;
6
+ lastPage: number;
7
+ firstPage: number;
8
+ firstPageUrl: null | string;
9
+ lastPageUrl: null | string;
10
+ nextPageUrl: null | string;
11
+ previousPageUrl: null | string;
12
+ };
13
+
14
+ interface Paginator$1<Result> extends Array<Result> {
15
+ all(): Result[];
16
+
17
+ readonly firstPage: number;
18
+ readonly perPage: number;
19
+ readonly currentPage: number;
20
+ readonly lastPage: number;
21
+ readonly hasPages: boolean;
22
+ readonly hasMorePages: boolean;
23
+ readonly isEmpty: boolean;
24
+ readonly total: number;
25
+ readonly hasTotal: boolean;
26
+
27
+ baseUrl(url: string): this;
28
+ queryString(values: { [key: string]: any }): this;
29
+ getUrl(page: number): string;
30
+ getMeta(): PaginationMeta;
31
+ getNextPageUrl(): string | null;
32
+ getPreviousPageUrl(): string | null;
33
+ getUrlsForRange(start: number, end: number): { url: string; page: number; isActive: boolean }[];
34
+ toJSON(): PaginationResult<Result>;
35
+ }
36
+
37
+ declare class Paginator<T = any> extends Array implements Paginator$1<T> {
38
+ private readonly totalNumber;
39
+ readonly perPage: number;
40
+ currentPage: number;
41
+ private qs;
42
+ private url;
43
+ private readonly rows;
44
+ readonly firstPage: number;
45
+ readonly isEmpty: boolean;
46
+ constructor(totalNumber: number, perPage: number, currentPage: number, ...rows: any[]);
47
+ get hasTotal(): boolean;
48
+ get hasMorePages(): boolean;
49
+ get hasPages(): boolean;
50
+ get lastPage(): number;
51
+ get total(): number;
52
+ all(): any[];
53
+ getMeta(): {
54
+ total: number;
55
+ perPage: number;
56
+ page: number;
57
+ lastPage: number;
58
+ firstPage: number;
59
+ firstPageUrl: string;
60
+ lastPageUrl: string;
61
+ nextPageUrl: string | null;
62
+ previousPageUrl: string | null;
63
+ };
64
+ toJSON(): {
65
+ meta: {
66
+ total: number;
67
+ perPage: number;
68
+ page: number;
69
+ lastPage: number;
70
+ firstPage: number;
71
+ firstPageUrl: string;
72
+ lastPageUrl: string;
73
+ nextPageUrl: string | null;
74
+ previousPageUrl: string | null;
75
+ };
76
+ data: any[];
77
+ };
78
+ queryString(values: {
79
+ [key: string]: any;
80
+ }): this;
81
+ baseUrl(url: string): this;
82
+ getUrl(page: number): string;
83
+ getNextPageUrl(): string | null;
84
+ getPreviousPageUrl(): string | null;
85
+ getUrlsForRange(start: number, end: number): {
86
+ url: string;
87
+ page: number;
88
+ isActive: boolean;
89
+ }[];
90
+ }
91
+
92
+ declare const paginate: <Result>(page: number, perPage: number, total: number, rows: Result[]) => Paginator$1<Result>;
93
+
94
+ export { Paginator, Paginator$1 as PaginatorInterface, paginate };
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _class;// src/paginator.ts
2
+ var _qs = require('qs'); var _qs2 = _interopRequireDefault(_qs);
3
+ var Paginator = (_class = class extends Array {
4
+ constructor(totalNumber, perPage, currentPage, ...rows) {
5
+ super(...rows);_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);;
6
+ this.totalNumber = totalNumber;
7
+ this.perPage = perPage;
8
+ this.currentPage = currentPage;
9
+ this.totalNumber = Number(totalNumber);
10
+ this.rows = rows;
11
+ this.isEmpty = this.rows.length === 0;
12
+ }
13
+ __init() {this.qs = {}}
14
+ __init2() {this.url = "/"}
15
+
16
+ __init3() {this.firstPage = 1}
17
+
18
+ get hasTotal() {
19
+ return this.total > 0;
20
+ }
21
+ get hasMorePages() {
22
+ return this.lastPage > this.currentPage;
23
+ }
24
+ get hasPages() {
25
+ return this.lastPage !== 1;
26
+ }
27
+ get lastPage() {
28
+ return Math.max(Math.ceil(this.total / this.perPage), 1);
29
+ }
30
+ get total() {
31
+ return Number(this.totalNumber);
32
+ }
33
+ all() {
34
+ return this.rows;
35
+ }
36
+ getMeta() {
37
+ return {
38
+ total: this.total,
39
+ perPage: this.perPage,
40
+ page: this.currentPage,
41
+ lastPage: this.lastPage,
42
+ firstPage: this.firstPage,
43
+ firstPageUrl: this.getUrl(1),
44
+ lastPageUrl: this.getUrl(this.lastPage),
45
+ nextPageUrl: this.getNextPageUrl(),
46
+ previousPageUrl: this.getPreviousPageUrl()
47
+ };
48
+ }
49
+ toJSON() {
50
+ return {
51
+ meta: this.getMeta(),
52
+ data: this.all()
53
+ };
54
+ }
55
+ queryString(values) {
56
+ this.qs = values;
57
+ return this;
58
+ }
59
+ baseUrl(url) {
60
+ this.url = url;
61
+ return this;
62
+ }
63
+ getUrl(page) {
64
+ const qstring = _qs2.default.stringify({ ...this.qs, page: page < 1 ? 1 : page });
65
+ return `${this.url}?${qstring}`;
66
+ }
67
+ getNextPageUrl() {
68
+ if (this.hasMorePages) {
69
+ return this.getUrl(this.currentPage + 1);
70
+ }
71
+ return null;
72
+ }
73
+ getPreviousPageUrl() {
74
+ if (this.currentPage > 1) {
75
+ return this.getUrl(this.currentPage - 1);
76
+ }
77
+ return null;
78
+ }
79
+ getUrlsForRange(start, end) {
80
+ const urls = [];
81
+ for (let index = start; index <= end; index++) {
82
+ urls.push({ url: this.getUrl(index), page: index, isActive: index === this.currentPage });
83
+ }
84
+ return urls;
85
+ }
86
+ }, _class);
87
+
88
+ // src/index.ts
89
+ var paginate = (page, perPage, total, rows) => new Paginator(total, Number(perPage), Number(page), ...rows);
90
+
91
+
92
+
93
+ exports.Paginator = Paginator; exports.paginate = paginate;
94
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/paginator.ts","../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,QAAQ;AAQf,IAAqB,YAArB,cAAgD,MAA+B;AAAA,EAiB3E,YAA6B,aAAqC,SAAwB,gBAAwB,MAAa;AAC3H,UAAM,GAAG,IAAI;AADY;AAAqC;AAAwB;AAGtF,SAAK,cAAc,OAAO,WAAW;AAErC,SAAK,OAAO;AACZ,SAAK,UAAU,KAAK,KAAK,WAAW;AAAA,EACxC;AAAA,EAvBQ,KAA6B,CAAC;AAAA,EAE9B,MAAc;AAAA,EAEL;AAAA,EAKD,YAAoB;AAAA,EAKpB;AAAA,EAkBhB,IAAI,WAAoB;AACpB,WAAO,KAAK,QAAQ;AAAA,EACxB;AAAA,EAKA,IAAI,eAAwB;AACxB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA,EAKA,IAAI,WAAoB;AACpB,WAAO,KAAK,aAAa;AAAA,EAC7B;AAAA,EAKA,IAAI,WAAmB;AACnB,WAAO,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAO,GAAG,CAAC;AAAA,EAC3D;AAAA,EAMA,IAAI,QAAgB;AAChB,WAAO,OAAO,KAAK,WAAW;AAAA,EAClC;AAAA,EAKO,MAAM;AACT,WAAO,KAAK;AAAA,EAChB;AAAA,EAKO,UAAU;AACb,WAAO;AAAA,MACH,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK,OAAO,CAAC;AAAA,MAC3B,aAAa,KAAK,OAAO,KAAK,QAAQ;AAAA,MACtC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK,mBAAmB;AAAA,IAC7C;AAAA,EACJ;AAAA,EAMO,SAAS;AACZ,WAAO;AAAA,MACH,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK,IAAI;AAAA,IACnB;AAAA,EACJ;AAAA,EAKO,YAAY,QAAsC;AACrD,SAAK,KAAK;AAEV,WAAO;AAAA,EACX;AAAA,EAKO,QAAQ,KAAmB;AAC9B,SAAK,MAAM;AAEX,WAAO;AAAA,EACX;AAAA,EAMO,OAAO,MAAsB;AAChC,UAAM,UAAU,GAAG,UAAU,EAAE,GAAG,KAAK,IAAI,MAAM,OAAO,IAAI,IAAI,KAAK,CAAC;AAEtE,WAAO,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAKO,iBAAgC;AACnC,QAAI,KAAK,cAAc;AACnB,aAAO,KAAK,OAAO,KAAK,cAAc,CAAC;AAAA,IAC3C;AAEA,WAAO;AAAA,EACX;AAAA,EAKO,qBAAoC;AACvC,QAAI,KAAK,cAAc,GAAG;AACtB,aAAO,KAAK,OAAO,KAAK,cAAc,CAAC;AAAA,IAC3C;AAEA,WAAO;AAAA,EACX;AAAA,EAKO,gBAAgB,OAAe,KAAa;AAC/C,UAAM,OAA2D,CAAC;AAGlE,aAAS,QAAQ,OAAO,SAAS,KAAK,SAAS;AAC3C,WAAK,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,GAAG,MAAM,OAAO,UAAU,UAAU,KAAK,YAAY,CAAC;AAAA,IAC5F;AAEA,WAAO;AAAA,EACX;AACJ;;;ACrKO,IAAM,WAAW,CAAS,MAAc,SAAiB,OAAe,SAA+C,IAAI,UAAU,OAAO,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,GAAG,IAAI","sourcesContent":["import qs from \"qs\";\n\nimport type { Paginator as IPaginator } from \"./types\";\n\n/**\n * Simple paginator works with the data set provided by the standard\n * `offset` and `limit` based pagination.\n */\nexport default class Paginator<T = any> extends Array implements IPaginator<T> {\n private qs: { [key: string]: any } = {};\n\n private url: string = \"/\";\n\n private readonly rows: any[];\n\n /**\n * The first page is always 1\n */\n public readonly firstPage: number = 1;\n\n /**\n * Find if results set is empty or not\n */\n public readonly isEmpty: boolean;\n\n constructor(private readonly totalNumber: number, public readonly perPage: number, public currentPage: number, ...rows: any[]) {\n super(...rows);\n\n this.totalNumber = Number(totalNumber);\n\n this.rows = rows;\n this.isEmpty = this.rows.length === 0;\n }\n\n /**\n * Find if there are total records or not. This is not same as\n * `isEmpty`.\n *\n * The `isEmpty` reports about the current set of results. However, `hasTotal`\n * reports about the total number of records, regardless of the current.\n */\n get hasTotal(): boolean {\n return this.total > 0;\n }\n\n /**\n * Find if there are more pages to come\n */\n get hasMorePages(): boolean {\n return this.lastPage > this.currentPage;\n }\n\n /**\n * Find if there are enough results to be paginated or not\n */\n get hasPages(): boolean {\n return this.lastPage !== 1;\n }\n\n /**\n * The Last page number\n */\n get lastPage(): number {\n return Math.max(Math.ceil(this.total / this.perPage), 1);\n }\n\n /**\n * Casting `total` to a number. Later, we can think of situations\n * to cast it to a bigint\n */\n get total(): number {\n return Number(this.totalNumber);\n }\n\n /**\n * A reference to the result rows\n */\n public all() {\n return this.rows;\n }\n\n /**\n * Returns JSON meta data\n */\n public getMeta() {\n return {\n total: this.total,\n perPage: this.perPage,\n page: this.currentPage,\n lastPage: this.lastPage,\n firstPage: this.firstPage,\n firstPageUrl: this.getUrl(1),\n lastPageUrl: this.getUrl(this.lastPage),\n nextPageUrl: this.getNextPageUrl(),\n previousPageUrl: this.getPreviousPageUrl(),\n };\n }\n\n /**\n * Returns JSON representation of the paginated\n * data\n */\n public toJSON() {\n return {\n meta: this.getMeta(),\n data: this.all(),\n };\n }\n\n /**\n * Define query string to be appended to the pagination links\n */\n public queryString(values: { [key: string]: any }): this {\n this.qs = values;\n\n return this;\n }\n\n /**\n * Define base url for making the pagination links\n */\n public baseUrl(url: string): this {\n this.url = url;\n\n return this;\n }\n\n /**\n * Returns url for a given page. Doesn't validate the integrity of the\n * page\n */\n public getUrl(page: number): string {\n const qstring = qs.stringify({ ...this.qs, page: page < 1 ? 1 : page });\n\n return `${this.url}?${qstring}`;\n }\n\n /**\n * Returns url for the next page\n */\n public getNextPageUrl(): string | null {\n if (this.hasMorePages) {\n return this.getUrl(this.currentPage + 1);\n }\n\n return null;\n }\n\n /**\n * Returns URL for the previous page\n */\n public getPreviousPageUrl(): string | null {\n if (this.currentPage > 1) {\n return this.getUrl(this.currentPage - 1);\n }\n\n return null;\n }\n\n /**\n * Returns an array of urls under a given range\n */\n public getUrlsForRange(start: number, end: number) {\n const urls: { url: string; page: number; isActive: boolean }[] = [];\n\n // eslint-disable-next-line no-plusplus\n for (let index = start; index <= end; index++) {\n urls.push({ url: this.getUrl(index), page: index, isActive: index === this.currentPage });\n }\n\n return urls;\n }\n}\n","import Paginator from \"./paginator\";\nimport type { Paginator as PaginatorInterface } from \"./types\";\n\nexport type { Paginator as PaginatorInterface } from \"./types.d\";\nexport { default as Paginator } from \"./paginator\";\n\n// eslint-disable-next-line max-len\nexport const paginate = <Result>(page: number, perPage: number, total: number, rows: Result[]): PaginatorInterface<Result> => new Paginator(total, Number(perPage), Number(page), ...rows);\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,94 @@
1
+ // src/paginator.ts
2
+ import qs from "qs";
3
+ var Paginator = class extends Array {
4
+ constructor(totalNumber, perPage, currentPage, ...rows) {
5
+ super(...rows);
6
+ this.totalNumber = totalNumber;
7
+ this.perPage = perPage;
8
+ this.currentPage = currentPage;
9
+ this.totalNumber = Number(totalNumber);
10
+ this.rows = rows;
11
+ this.isEmpty = this.rows.length === 0;
12
+ }
13
+ qs = {};
14
+ url = "/";
15
+ rows;
16
+ firstPage = 1;
17
+ isEmpty;
18
+ get hasTotal() {
19
+ return this.total > 0;
20
+ }
21
+ get hasMorePages() {
22
+ return this.lastPage > this.currentPage;
23
+ }
24
+ get hasPages() {
25
+ return this.lastPage !== 1;
26
+ }
27
+ get lastPage() {
28
+ return Math.max(Math.ceil(this.total / this.perPage), 1);
29
+ }
30
+ get total() {
31
+ return Number(this.totalNumber);
32
+ }
33
+ all() {
34
+ return this.rows;
35
+ }
36
+ getMeta() {
37
+ return {
38
+ total: this.total,
39
+ perPage: this.perPage,
40
+ page: this.currentPage,
41
+ lastPage: this.lastPage,
42
+ firstPage: this.firstPage,
43
+ firstPageUrl: this.getUrl(1),
44
+ lastPageUrl: this.getUrl(this.lastPage),
45
+ nextPageUrl: this.getNextPageUrl(),
46
+ previousPageUrl: this.getPreviousPageUrl()
47
+ };
48
+ }
49
+ toJSON() {
50
+ return {
51
+ meta: this.getMeta(),
52
+ data: this.all()
53
+ };
54
+ }
55
+ queryString(values) {
56
+ this.qs = values;
57
+ return this;
58
+ }
59
+ baseUrl(url) {
60
+ this.url = url;
61
+ return this;
62
+ }
63
+ getUrl(page) {
64
+ const qstring = qs.stringify({ ...this.qs, page: page < 1 ? 1 : page });
65
+ return `${this.url}?${qstring}`;
66
+ }
67
+ getNextPageUrl() {
68
+ if (this.hasMorePages) {
69
+ return this.getUrl(this.currentPage + 1);
70
+ }
71
+ return null;
72
+ }
73
+ getPreviousPageUrl() {
74
+ if (this.currentPage > 1) {
75
+ return this.getUrl(this.currentPage - 1);
76
+ }
77
+ return null;
78
+ }
79
+ getUrlsForRange(start, end) {
80
+ const urls = [];
81
+ for (let index = start; index <= end; index++) {
82
+ urls.push({ url: this.getUrl(index), page: index, isActive: index === this.currentPage });
83
+ }
84
+ return urls;
85
+ }
86
+ };
87
+
88
+ // src/index.ts
89
+ var paginate = (page, perPage, total, rows) => new Paginator(total, Number(perPage), Number(page), ...rows);
90
+ export {
91
+ Paginator,
92
+ paginate
93
+ };
94
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/paginator.ts","../src/index.ts"],"sourcesContent":["import qs from \"qs\";\n\nimport type { Paginator as IPaginator } from \"./types\";\n\n/**\n * Simple paginator works with the data set provided by the standard\n * `offset` and `limit` based pagination.\n */\nexport default class Paginator<T = any> extends Array implements IPaginator<T> {\n private qs: { [key: string]: any } = {};\n\n private url: string = \"/\";\n\n private readonly rows: any[];\n\n /**\n * The first page is always 1\n */\n public readonly firstPage: number = 1;\n\n /**\n * Find if results set is empty or not\n */\n public readonly isEmpty: boolean;\n\n constructor(private readonly totalNumber: number, public readonly perPage: number, public currentPage: number, ...rows: any[]) {\n super(...rows);\n\n this.totalNumber = Number(totalNumber);\n\n this.rows = rows;\n this.isEmpty = this.rows.length === 0;\n }\n\n /**\n * Find if there are total records or not. This is not same as\n * `isEmpty`.\n *\n * The `isEmpty` reports about the current set of results. However, `hasTotal`\n * reports about the total number of records, regardless of the current.\n */\n get hasTotal(): boolean {\n return this.total > 0;\n }\n\n /**\n * Find if there are more pages to come\n */\n get hasMorePages(): boolean {\n return this.lastPage > this.currentPage;\n }\n\n /**\n * Find if there are enough results to be paginated or not\n */\n get hasPages(): boolean {\n return this.lastPage !== 1;\n }\n\n /**\n * The Last page number\n */\n get lastPage(): number {\n return Math.max(Math.ceil(this.total / this.perPage), 1);\n }\n\n /**\n * Casting `total` to a number. Later, we can think of situations\n * to cast it to a bigint\n */\n get total(): number {\n return Number(this.totalNumber);\n }\n\n /**\n * A reference to the result rows\n */\n public all() {\n return this.rows;\n }\n\n /**\n * Returns JSON meta data\n */\n public getMeta() {\n return {\n total: this.total,\n perPage: this.perPage,\n page: this.currentPage,\n lastPage: this.lastPage,\n firstPage: this.firstPage,\n firstPageUrl: this.getUrl(1),\n lastPageUrl: this.getUrl(this.lastPage),\n nextPageUrl: this.getNextPageUrl(),\n previousPageUrl: this.getPreviousPageUrl(),\n };\n }\n\n /**\n * Returns JSON representation of the paginated\n * data\n */\n public toJSON() {\n return {\n meta: this.getMeta(),\n data: this.all(),\n };\n }\n\n /**\n * Define query string to be appended to the pagination links\n */\n public queryString(values: { [key: string]: any }): this {\n this.qs = values;\n\n return this;\n }\n\n /**\n * Define base url for making the pagination links\n */\n public baseUrl(url: string): this {\n this.url = url;\n\n return this;\n }\n\n /**\n * Returns url for a given page. Doesn't validate the integrity of the\n * page\n */\n public getUrl(page: number): string {\n const qstring = qs.stringify({ ...this.qs, page: page < 1 ? 1 : page });\n\n return `${this.url}?${qstring}`;\n }\n\n /**\n * Returns url for the next page\n */\n public getNextPageUrl(): string | null {\n if (this.hasMorePages) {\n return this.getUrl(this.currentPage + 1);\n }\n\n return null;\n }\n\n /**\n * Returns URL for the previous page\n */\n public getPreviousPageUrl(): string | null {\n if (this.currentPage > 1) {\n return this.getUrl(this.currentPage - 1);\n }\n\n return null;\n }\n\n /**\n * Returns an array of urls under a given range\n */\n public getUrlsForRange(start: number, end: number) {\n const urls: { url: string; page: number; isActive: boolean }[] = [];\n\n // eslint-disable-next-line no-plusplus\n for (let index = start; index <= end; index++) {\n urls.push({ url: this.getUrl(index), page: index, isActive: index === this.currentPage });\n }\n\n return urls;\n }\n}\n","import Paginator from \"./paginator\";\nimport type { Paginator as PaginatorInterface } from \"./types\";\n\nexport type { Paginator as PaginatorInterface } from \"./types.d\";\nexport { default as Paginator } from \"./paginator\";\n\n// eslint-disable-next-line max-len\nexport const paginate = <Result>(page: number, perPage: number, total: number, rows: Result[]): PaginatorInterface<Result> => new Paginator(total, Number(perPage), Number(page), ...rows);\n"],"mappings":";AAAA,OAAO,QAAQ;AAQf,IAAqB,YAArB,cAAgD,MAA+B;AAAA,EAiB3E,YAA6B,aAAqC,SAAwB,gBAAwB,MAAa;AAC3H,UAAM,GAAG,IAAI;AADY;AAAqC;AAAwB;AAGtF,SAAK,cAAc,OAAO,WAAW;AAErC,SAAK,OAAO;AACZ,SAAK,UAAU,KAAK,KAAK,WAAW;AAAA,EACxC;AAAA,EAvBQ,KAA6B,CAAC;AAAA,EAE9B,MAAc;AAAA,EAEL;AAAA,EAKD,YAAoB;AAAA,EAKpB;AAAA,EAkBhB,IAAI,WAAoB;AACpB,WAAO,KAAK,QAAQ;AAAA,EACxB;AAAA,EAKA,IAAI,eAAwB;AACxB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA,EAKA,IAAI,WAAoB;AACpB,WAAO,KAAK,aAAa;AAAA,EAC7B;AAAA,EAKA,IAAI,WAAmB;AACnB,WAAO,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAO,GAAG,CAAC;AAAA,EAC3D;AAAA,EAMA,IAAI,QAAgB;AAChB,WAAO,OAAO,KAAK,WAAW;AAAA,EAClC;AAAA,EAKO,MAAM;AACT,WAAO,KAAK;AAAA,EAChB;AAAA,EAKO,UAAU;AACb,WAAO;AAAA,MACH,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK,OAAO,CAAC;AAAA,MAC3B,aAAa,KAAK,OAAO,KAAK,QAAQ;AAAA,MACtC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK,mBAAmB;AAAA,IAC7C;AAAA,EACJ;AAAA,EAMO,SAAS;AACZ,WAAO;AAAA,MACH,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK,IAAI;AAAA,IACnB;AAAA,EACJ;AAAA,EAKO,YAAY,QAAsC;AACrD,SAAK,KAAK;AAEV,WAAO;AAAA,EACX;AAAA,EAKO,QAAQ,KAAmB;AAC9B,SAAK,MAAM;AAEX,WAAO;AAAA,EACX;AAAA,EAMO,OAAO,MAAsB;AAChC,UAAM,UAAU,GAAG,UAAU,EAAE,GAAG,KAAK,IAAI,MAAM,OAAO,IAAI,IAAI,KAAK,CAAC;AAEtE,WAAO,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAKO,iBAAgC;AACnC,QAAI,KAAK,cAAc;AACnB,aAAO,KAAK,OAAO,KAAK,cAAc,CAAC;AAAA,IAC3C;AAEA,WAAO;AAAA,EACX;AAAA,EAKO,qBAAoC;AACvC,QAAI,KAAK,cAAc,GAAG;AACtB,aAAO,KAAK,OAAO,KAAK,cAAc,CAAC;AAAA,IAC3C;AAEA,WAAO;AAAA,EACX;AAAA,EAKO,gBAAgB,OAAe,KAAa;AAC/C,UAAM,OAA2D,CAAC;AAGlE,aAAS,QAAQ,OAAO,SAAS,KAAK,SAAS;AAC3C,WAAK,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,GAAG,MAAM,OAAO,UAAU,UAAU,KAAK,YAAY,CAAC;AAAA,IAC5F;AAEA,WAAO;AAAA,EACX;AACJ;;;ACrKO,IAAM,WAAW,CAAS,MAAc,SAAiB,OAAe,SAA+C,IAAI,UAAU,OAAO,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,GAAG,IAAI;","names":[]}
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "@visulima/pagination",
3
+ "version": "1.0.0",
4
+ "description": "Pagination for node.",
5
+ "keywords": [
6
+ "anolilab",
7
+ "pagination",
8
+ "paginator",
9
+ "offset"
10
+ ],
11
+ "homepage": "https://visulima.com/packages/pagination",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/visulima/visulima.git",
15
+ "directory": "packages/pagination"
16
+ },
17
+ "funding": [
18
+ {
19
+ "type": "github",
20
+ "url": "https://github.com/sponsors/prisis"
21
+ },
22
+ {
23
+ "type": "consulting",
24
+ "url": "https://anolilab.com/support"
25
+ }
26
+ ],
27
+ "license": "MIT",
28
+ "author": {
29
+ "name": "Daniel Bannert",
30
+ "email": "d.bannert@anolilab.de"
31
+ },
32
+ "sideEffects": false,
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "require": "./dist/index.js",
37
+ "import": "./dist/index.mjs"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "main": "dist/index.js",
42
+ "module": "dist/index.module.mjs",
43
+ "source": "src/index.ts",
44
+ "types": "dist/index.d.ts",
45
+ "files": [
46
+ "src",
47
+ "dist",
48
+ "README.md",
49
+ "CHANGELOG.md",
50
+ "LICENSE.md"
51
+ ],
52
+ "scripts": {
53
+ "build": "cross-env NODE_ENV=development tsup",
54
+ "build:prod": "cross-env NODE_ENV=production tsup",
55
+ "clean": "rimraf node_modules dist",
56
+ "coverage": "vitest run --coverage",
57
+ "dev": "pnpm predev && pnpm run build --watch",
58
+ "lint:eslint": "cross-env NO_LOGS=true eslint --ext js,jsx,ts,tsx --max-warnings=0 --config .eslintrc.cjs",
59
+ "lint:eslint:fix": "pnpm run lint:eslint --fix",
60
+ "test": "vitest"
61
+ },
62
+ "dependencies": {
63
+ "qs": "^6.11.0"
64
+ },
65
+ "devDependencies": {
66
+ "@anolilab/eslint-config": "^4.0.9",
67
+ "@anolilab/semantic-release-preset": "^2.0.7",
68
+ "@rushstack/eslint-plugin-security": "^0.5.0",
69
+ "@types/micromatch": "^4.0.2",
70
+ "@types/node": "^18.8.4",
71
+ "@types/qs": "^6.9.7",
72
+ "@typescript-eslint/eslint-plugin": "^5.40.0",
73
+ "@typescript-eslint/parser": "^5.40.0",
74
+ "cross-env": "^7.0.3",
75
+ "eslint": "^8.25.0",
76
+ "eslint-plugin-compat": "^4.0.2",
77
+ "eslint-plugin-eslint-comments": "^3.2.0",
78
+ "eslint-plugin-import": "^2.26.0",
79
+ "eslint-plugin-json": "^3.1.0",
80
+ "eslint-plugin-jsonc": "^2.5.0",
81
+ "eslint-plugin-jsx-a11y": "^6.6.1",
82
+ "eslint-plugin-markdown": "^3.0.0",
83
+ "eslint-plugin-material-ui": "^1.0.1",
84
+ "eslint-plugin-no-loops": "^0.3.0",
85
+ "eslint-plugin-no-secrets": "^0.8.9",
86
+ "eslint-plugin-node": "^11.1.0",
87
+ "eslint-plugin-optimize-regex": "^1.2.1",
88
+ "eslint-plugin-promise": "^6.0.1",
89
+ "eslint-plugin-radar": "^0.2.1",
90
+ "eslint-plugin-react": "7.31.10",
91
+ "eslint-plugin-react-hooks": "4.6.0",
92
+ "eslint-plugin-simple-import-sort": "^8.0.0",
93
+ "eslint-plugin-sort-keys-fix": "^1.1.2",
94
+ "eslint-plugin-testing-library": "^5.7.2",
95
+ "eslint-plugin-unicorn": "^44.0.2",
96
+ "eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0",
97
+ "eslint-plugin-you-dont-need-momentjs": "^1.6.0",
98
+ "prettier": "^2.7.1",
99
+ "read-pkg": "^7.1.0",
100
+ "rimraf": "^3.0.2",
101
+ "semantic-release": "^19.0.5",
102
+ "tsup": "^6.2.3",
103
+ "typescript": "^4.8.4",
104
+ "vitest": "^0.24.1"
105
+ },
106
+ "engines": {
107
+ "node": ">=16"
108
+ },
109
+ "publishConfig": {
110
+ "access": "public"
111
+ }
112
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import Paginator from "./paginator";
2
+ import type { Paginator as PaginatorInterface } from "./types";
3
+
4
+ export type { Paginator as PaginatorInterface } from "./types.d";
5
+ export { default as Paginator } from "./paginator";
6
+
7
+ // eslint-disable-next-line max-len
8
+ export const paginate = <Result>(page: number, perPage: number, total: number, rows: Result[]): PaginatorInterface<Result> => new Paginator(total, Number(perPage), Number(page), ...rows);
@@ -0,0 +1,173 @@
1
+ import qs from "qs";
2
+
3
+ import type { Paginator as IPaginator } from "./types";
4
+
5
+ /**
6
+ * Simple paginator works with the data set provided by the standard
7
+ * `offset` and `limit` based pagination.
8
+ */
9
+ export default class Paginator<T = any> extends Array implements IPaginator<T> {
10
+ private qs: { [key: string]: any } = {};
11
+
12
+ private url: string = "/";
13
+
14
+ private readonly rows: any[];
15
+
16
+ /**
17
+ * The first page is always 1
18
+ */
19
+ public readonly firstPage: number = 1;
20
+
21
+ /**
22
+ * Find if results set is empty or not
23
+ */
24
+ public readonly isEmpty: boolean;
25
+
26
+ constructor(private readonly totalNumber: number, public readonly perPage: number, public currentPage: number, ...rows: any[]) {
27
+ super(...rows);
28
+
29
+ this.totalNumber = Number(totalNumber);
30
+
31
+ this.rows = rows;
32
+ this.isEmpty = this.rows.length === 0;
33
+ }
34
+
35
+ /**
36
+ * Find if there are total records or not. This is not same as
37
+ * `isEmpty`.
38
+ *
39
+ * The `isEmpty` reports about the current set of results. However, `hasTotal`
40
+ * reports about the total number of records, regardless of the current.
41
+ */
42
+ get hasTotal(): boolean {
43
+ return this.total > 0;
44
+ }
45
+
46
+ /**
47
+ * Find if there are more pages to come
48
+ */
49
+ get hasMorePages(): boolean {
50
+ return this.lastPage > this.currentPage;
51
+ }
52
+
53
+ /**
54
+ * Find if there are enough results to be paginated or not
55
+ */
56
+ get hasPages(): boolean {
57
+ return this.lastPage !== 1;
58
+ }
59
+
60
+ /**
61
+ * The Last page number
62
+ */
63
+ get lastPage(): number {
64
+ return Math.max(Math.ceil(this.total / this.perPage), 1);
65
+ }
66
+
67
+ /**
68
+ * Casting `total` to a number. Later, we can think of situations
69
+ * to cast it to a bigint
70
+ */
71
+ get total(): number {
72
+ return Number(this.totalNumber);
73
+ }
74
+
75
+ /**
76
+ * A reference to the result rows
77
+ */
78
+ public all() {
79
+ return this.rows;
80
+ }
81
+
82
+ /**
83
+ * Returns JSON meta data
84
+ */
85
+ public getMeta() {
86
+ return {
87
+ total: this.total,
88
+ perPage: this.perPage,
89
+ page: this.currentPage,
90
+ lastPage: this.lastPage,
91
+ firstPage: this.firstPage,
92
+ firstPageUrl: this.getUrl(1),
93
+ lastPageUrl: this.getUrl(this.lastPage),
94
+ nextPageUrl: this.getNextPageUrl(),
95
+ previousPageUrl: this.getPreviousPageUrl(),
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Returns JSON representation of the paginated
101
+ * data
102
+ */
103
+ public toJSON() {
104
+ return {
105
+ meta: this.getMeta(),
106
+ data: this.all(),
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Define query string to be appended to the pagination links
112
+ */
113
+ public queryString(values: { [key: string]: any }): this {
114
+ this.qs = values;
115
+
116
+ return this;
117
+ }
118
+
119
+ /**
120
+ * Define base url for making the pagination links
121
+ */
122
+ public baseUrl(url: string): this {
123
+ this.url = url;
124
+
125
+ return this;
126
+ }
127
+
128
+ /**
129
+ * Returns url for a given page. Doesn't validate the integrity of the
130
+ * page
131
+ */
132
+ public getUrl(page: number): string {
133
+ const qstring = qs.stringify({ ...this.qs, page: page < 1 ? 1 : page });
134
+
135
+ return `${this.url}?${qstring}`;
136
+ }
137
+
138
+ /**
139
+ * Returns url for the next page
140
+ */
141
+ public getNextPageUrl(): string | null {
142
+ if (this.hasMorePages) {
143
+ return this.getUrl(this.currentPage + 1);
144
+ }
145
+
146
+ return null;
147
+ }
148
+
149
+ /**
150
+ * Returns URL for the previous page
151
+ */
152
+ public getPreviousPageUrl(): string | null {
153
+ if (this.currentPage > 1) {
154
+ return this.getUrl(this.currentPage - 1);
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * Returns an array of urls under a given range
162
+ */
163
+ public getUrlsForRange(start: number, end: number) {
164
+ const urls: { url: string; page: number; isActive: boolean }[] = [];
165
+
166
+ // eslint-disable-next-line no-plusplus
167
+ for (let index = start; index <= end; index++) {
168
+ urls.push({ url: this.getUrl(index), page: index, isActive: index === this.currentPage });
169
+ }
170
+
171
+ return urls;
172
+ }
173
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ export type PaginationResult<Result> = { meta: PaginationMeta; data: Result[] };
2
+ export type PaginationMeta = {
3
+ total: number;
4
+ perPage: number;
5
+ page: number;
6
+ lastPage: number;
7
+ firstPage: number;
8
+ firstPageUrl: null | string;
9
+ lastPageUrl: null | string;
10
+ nextPageUrl: null | string;
11
+ previousPageUrl: null | string;
12
+ };
13
+
14
+ export interface Paginator<Result> extends Array<Result> {
15
+ all(): Result[];
16
+
17
+ readonly firstPage: number;
18
+ readonly perPage: number;
19
+ readonly currentPage: number;
20
+ readonly lastPage: number;
21
+ readonly hasPages: boolean;
22
+ readonly hasMorePages: boolean;
23
+ readonly isEmpty: boolean;
24
+ readonly total: number;
25
+ readonly hasTotal: boolean;
26
+
27
+ baseUrl(url: string): this;
28
+ queryString(values: { [key: string]: any }): this;
29
+ getUrl(page: number): string;
30
+ getMeta(): PaginationMeta;
31
+ getNextPageUrl(): string | null;
32
+ getPreviousPageUrl(): string | null;
33
+ getUrlsForRange(start: number, end: number): { url: string; page: number; isActive: boolean }[];
34
+ toJSON(): PaginationResult<Result>;
35
+ }