@startupjs-ui/pagination 0.1.3
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 +20 -0
- package/README.mdx +165 -0
- package/index.cssx.styl +21 -0
- package/index.d.ts +49 -0
- package/index.tsx +140 -0
- package/package.json +22 -0
- package/usePagination.ts +212 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/pagination
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
|
|
20
|
+
* **pagination:** refactor Pagination component ([469d5f3](https://github.com/startupjs/startupjs-ui/commit/469d5f35b5d32122622a627d68832ec9a7b34729))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { $ } from 'startupjs'
|
|
3
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
4
|
+
import Div from '@startupjs-ui/div'
|
|
5
|
+
import Pagination, { _PropsJsonSchema as PaginationPropsJsonSchema } from './index'
|
|
6
|
+
|
|
7
|
+
# Pagination
|
|
8
|
+
|
|
9
|
+
The Pagination component enables the user to select a specific page from a range of pages.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import { Pagination } from 'startupjs-ui'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Simple example
|
|
16
|
+
|
|
17
|
+
- `pages` specifies the total number of pages
|
|
18
|
+
- `page` specifies the current page, starting from `0` to `pages - 1`
|
|
19
|
+
|
|
20
|
+
```jsx example
|
|
21
|
+
const [page, setPage] = useState(0)
|
|
22
|
+
return (
|
|
23
|
+
<Div row align='center'>
|
|
24
|
+
<Pagination
|
|
25
|
+
page={page}
|
|
26
|
+
pages={10}
|
|
27
|
+
onChangePage={setPage}
|
|
28
|
+
/>
|
|
29
|
+
</Div>
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Alternatives
|
|
34
|
+
|
|
35
|
+
- `count` - total count of items
|
|
36
|
+
- `limit` - count of items to display per page
|
|
37
|
+
- `skip` - count of items to skip
|
|
38
|
+
|
|
39
|
+
You can use them instead of `pages` or `page` properties:
|
|
40
|
+
|
|
41
|
+
- `count` and `limit` instead of `pages`
|
|
42
|
+
- `limit` and `skip` instead of `page`
|
|
43
|
+
|
|
44
|
+
```jsx example
|
|
45
|
+
const COUNT = 100
|
|
46
|
+
const LIMIT = 10
|
|
47
|
+
const [skip, setSkip] = useState(0)
|
|
48
|
+
return (
|
|
49
|
+
<Div row align='center'>
|
|
50
|
+
<Pagination
|
|
51
|
+
count={COUNT}
|
|
52
|
+
limit={LIMIT}
|
|
53
|
+
skip={skip}
|
|
54
|
+
onChangePage={(val) => setSkip(val * LIMIT)}
|
|
55
|
+
/>
|
|
56
|
+
</Div>
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Two-way data bindings
|
|
61
|
+
|
|
62
|
+
You can use two-way data bindings `$page`, `$limit`, `$skip` instead of `page`, `limit`, `skip` respectively.
|
|
63
|
+
|
|
64
|
+
```jsx example
|
|
65
|
+
const $page = $(0)
|
|
66
|
+
return (
|
|
67
|
+
<Div row align='center'>
|
|
68
|
+
<Pagination
|
|
69
|
+
$page={$page}
|
|
70
|
+
pages={10}
|
|
71
|
+
/>
|
|
72
|
+
</Div>
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```jsx example
|
|
77
|
+
const LIMIT = 10
|
|
78
|
+
const $skip = $(0)
|
|
79
|
+
return (
|
|
80
|
+
<Div row align='center'>
|
|
81
|
+
<Pagination
|
|
82
|
+
pages={10}
|
|
83
|
+
limit={LIMIT}
|
|
84
|
+
$skip={$skip}
|
|
85
|
+
onChangePage={(val) => $skip.set(val * LIMIT)}
|
|
86
|
+
/>
|
|
87
|
+
</Div>
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Buttons
|
|
92
|
+
|
|
93
|
+
- `showPrevButton` displays the previous-page button, (default `true`)
|
|
94
|
+
- `showNextButton` displays the next-page button (default `true`)
|
|
95
|
+
- `showFirstButton` displays the first-page button (default `false`)
|
|
96
|
+
- `showLastButton` displays the last-page button (default `false`)
|
|
97
|
+
|
|
98
|
+
```jsx example
|
|
99
|
+
const [page, setPage] = useState(0)
|
|
100
|
+
return (
|
|
101
|
+
<Div row align='center'>
|
|
102
|
+
<Pagination
|
|
103
|
+
page={page}
|
|
104
|
+
pages={10}
|
|
105
|
+
showPrevButton={false}
|
|
106
|
+
showNextButton={false}
|
|
107
|
+
showFirstButton
|
|
108
|
+
showLastButton
|
|
109
|
+
onChangePage={setPage}
|
|
110
|
+
/>
|
|
111
|
+
</Div>
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Compact view
|
|
116
|
+
|
|
117
|
+
To display the component in a compact view pass `variant='compact'`.
|
|
118
|
+
|
|
119
|
+
```jsx example
|
|
120
|
+
const [page, setPage] = useState(0)
|
|
121
|
+
return (
|
|
122
|
+
<Div row align='center'>
|
|
123
|
+
<Pagination
|
|
124
|
+
variant='compact'
|
|
125
|
+
page={page}
|
|
126
|
+
pages={10}
|
|
127
|
+
onChangePage={setPage}
|
|
128
|
+
/>
|
|
129
|
+
</Div>
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Visible pages
|
|
134
|
+
|
|
135
|
+
You can control the visibility of pages when component has default view (`variant='full'`).
|
|
136
|
+
|
|
137
|
+
- `boundaryCount` controls the number of visible pages at the beginning and end
|
|
138
|
+
- `siblingCount` controls the number of visible pages before and after the current page
|
|
139
|
+
|
|
140
|
+
```jsx example
|
|
141
|
+
const [page, setPage] = useState(5)
|
|
142
|
+
return (
|
|
143
|
+
<Div row align='center'>
|
|
144
|
+
<Pagination
|
|
145
|
+
page={page}
|
|
146
|
+
boundaryCount={2}
|
|
147
|
+
siblingCount={0}
|
|
148
|
+
pages={10}
|
|
149
|
+
onChangePage={setPage}
|
|
150
|
+
/>
|
|
151
|
+
</Div>
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Sandbox
|
|
156
|
+
|
|
157
|
+
<Sandbox
|
|
158
|
+
Component={Pagination}
|
|
159
|
+
propsJsonSchema={PaginationPropsJsonSchema}
|
|
160
|
+
props={{
|
|
161
|
+
page: $.session.Props.Pagination.page.get(),
|
|
162
|
+
onChangePage: page => $.session.Props.Pagination.page.set(page)
|
|
163
|
+
}}
|
|
164
|
+
$props={$.session.Props.Pagination}
|
|
165
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.item
|
|
2
|
+
min-width 4u
|
|
3
|
+
height 4u
|
|
4
|
+
align-items center
|
|
5
|
+
justify-content center
|
|
6
|
+
padding-left 1u
|
|
7
|
+
padding-right @padding-left
|
|
8
|
+
|
|
9
|
+
.page
|
|
10
|
+
&.selected
|
|
11
|
+
color var(--color-text-primary)
|
|
12
|
+
|
|
13
|
+
.status
|
|
14
|
+
margin-left 1u
|
|
15
|
+
margin-right @margin-left
|
|
16
|
+
|
|
17
|
+
.icon
|
|
18
|
+
color var(--color-text-secondary)
|
|
19
|
+
|
|
20
|
+
&.disabled
|
|
21
|
+
color var(--color-text-placeholder)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
6
|
+
import './index.cssx.styl';
|
|
7
|
+
declare const _default: React.ComponentType<PaginationProps>;
|
|
8
|
+
export default _default;
|
|
9
|
+
export declare const _PropsJsonSchema: {};
|
|
10
|
+
export interface PaginationProps {
|
|
11
|
+
/** Custom styles applied to the root container */
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
13
|
+
/** Display variant controlling layout @default 'full' */
|
|
14
|
+
variant?: 'full' | 'compact';
|
|
15
|
+
/** Zero-based page index */
|
|
16
|
+
page?: number;
|
|
17
|
+
/** Scoped model for page index */
|
|
18
|
+
$page?: any;
|
|
19
|
+
/** Total number of pages */
|
|
20
|
+
pages?: number;
|
|
21
|
+
/** Number of items to skip before current page @default 0 */
|
|
22
|
+
skip?: number;
|
|
23
|
+
/** Scoped model for skip value */
|
|
24
|
+
$skip?: any;
|
|
25
|
+
/** Number of items per page @default 1 */
|
|
26
|
+
limit?: number;
|
|
27
|
+
/** Scoped model for limit value */
|
|
28
|
+
$limit?: any;
|
|
29
|
+
/** Total number of items @default 0 */
|
|
30
|
+
count?: number;
|
|
31
|
+
/** Visible pages at the start and end @default 1 */
|
|
32
|
+
boundaryCount?: number;
|
|
33
|
+
/** Visible sibling pages around the current page @default 1 */
|
|
34
|
+
siblingCount?: number;
|
|
35
|
+
/** Show button for the first page @default false */
|
|
36
|
+
showFirstButton?: boolean;
|
|
37
|
+
/** Show button for the last page @default false */
|
|
38
|
+
showLastButton?: boolean;
|
|
39
|
+
/** Show previous page button @default true */
|
|
40
|
+
showPrevButton?: boolean;
|
|
41
|
+
/** Show next page button @default true */
|
|
42
|
+
showNextButton?: boolean;
|
|
43
|
+
/** Disable all navigation @default false */
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
/** Called when the page changes */
|
|
46
|
+
onChangePage?: (page: number) => void;
|
|
47
|
+
/** Called when the page size changes */
|
|
48
|
+
onChangeLimit?: (limit: number) => void;
|
|
49
|
+
}
|
package/index.tsx
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react'
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Div from '@startupjs-ui/div'
|
|
6
|
+
import Icon from '@startupjs-ui/icon'
|
|
7
|
+
import Span from '@startupjs-ui/span'
|
|
8
|
+
import { faAngleLeft } from '@fortawesome/free-solid-svg-icons/faAngleLeft'
|
|
9
|
+
import { faAngleDoubleLeft } from '@fortawesome/free-solid-svg-icons/faAngleDoubleLeft'
|
|
10
|
+
import { faAngleRight } from '@fortawesome/free-solid-svg-icons/faAngleRight'
|
|
11
|
+
import { faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons/faAngleDoubleRight'
|
|
12
|
+
import usePagination from './usePagination'
|
|
13
|
+
import './index.cssx.styl'
|
|
14
|
+
|
|
15
|
+
const ICONS = {
|
|
16
|
+
first: faAngleDoubleLeft,
|
|
17
|
+
last: faAngleDoubleRight,
|
|
18
|
+
previous: faAngleLeft,
|
|
19
|
+
next: faAngleRight
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default observer(themed('Pagination', Pagination))
|
|
23
|
+
|
|
24
|
+
export const _PropsJsonSchema = {/* PaginationProps */} // used in docs generation
|
|
25
|
+
|
|
26
|
+
export interface PaginationProps {
|
|
27
|
+
/** Custom styles applied to the root container */
|
|
28
|
+
style?: StyleProp<ViewStyle>
|
|
29
|
+
/** Display variant controlling layout @default 'full' */
|
|
30
|
+
variant?: 'full' | 'compact'
|
|
31
|
+
/** Zero-based page index */
|
|
32
|
+
page?: number
|
|
33
|
+
/** Scoped model for page index */
|
|
34
|
+
$page?: any
|
|
35
|
+
/** Total number of pages */
|
|
36
|
+
pages?: number
|
|
37
|
+
/** Number of items to skip before current page @default 0 */
|
|
38
|
+
skip?: number
|
|
39
|
+
/** Scoped model for skip value */
|
|
40
|
+
$skip?: any
|
|
41
|
+
/** Number of items per page @default 1 */
|
|
42
|
+
limit?: number
|
|
43
|
+
/** Scoped model for limit value */
|
|
44
|
+
$limit?: any
|
|
45
|
+
/** Total number of items @default 0 */
|
|
46
|
+
count?: number
|
|
47
|
+
/** Visible pages at the start and end @default 1 */
|
|
48
|
+
boundaryCount?: number
|
|
49
|
+
/** Visible sibling pages around the current page @default 1 */
|
|
50
|
+
siblingCount?: number
|
|
51
|
+
/** Show button for the first page @default false */
|
|
52
|
+
showFirstButton?: boolean
|
|
53
|
+
/** Show button for the last page @default false */
|
|
54
|
+
showLastButton?: boolean
|
|
55
|
+
/** Show previous page button @default true */
|
|
56
|
+
showPrevButton?: boolean
|
|
57
|
+
/** Show next page button @default true */
|
|
58
|
+
showNextButton?: boolean
|
|
59
|
+
/** Disable all navigation @default false */
|
|
60
|
+
disabled?: boolean
|
|
61
|
+
/** Called when the page changes */
|
|
62
|
+
onChangePage?: (page: number) => void
|
|
63
|
+
/** Called when the page size changes */
|
|
64
|
+
onChangeLimit?: (limit: number) => void
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Pagination ({
|
|
68
|
+
style,
|
|
69
|
+
variant = 'full',
|
|
70
|
+
page,
|
|
71
|
+
$page,
|
|
72
|
+
pages,
|
|
73
|
+
skip = 0,
|
|
74
|
+
$skip,
|
|
75
|
+
limit = 1,
|
|
76
|
+
$limit,
|
|
77
|
+
count = 0,
|
|
78
|
+
boundaryCount = 1, // min 1
|
|
79
|
+
siblingCount = 1, // min 0
|
|
80
|
+
showFirstButton = false,
|
|
81
|
+
showLastButton = false,
|
|
82
|
+
showPrevButton = true,
|
|
83
|
+
showNextButton = true,
|
|
84
|
+
disabled = false,
|
|
85
|
+
onChangePage,
|
|
86
|
+
onChangeLimit
|
|
87
|
+
}: PaginationProps): ReactNode {
|
|
88
|
+
const items = usePagination({
|
|
89
|
+
variant,
|
|
90
|
+
page,
|
|
91
|
+
$page,
|
|
92
|
+
pages,
|
|
93
|
+
skip,
|
|
94
|
+
$skip,
|
|
95
|
+
limit,
|
|
96
|
+
$limit,
|
|
97
|
+
count,
|
|
98
|
+
boundaryCount,
|
|
99
|
+
siblingCount,
|
|
100
|
+
showFirstButton,
|
|
101
|
+
showLastButton,
|
|
102
|
+
showPrevButton,
|
|
103
|
+
showNextButton,
|
|
104
|
+
disabled,
|
|
105
|
+
onChangePage,
|
|
106
|
+
onChangeLimit
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return pug`
|
|
110
|
+
Div(row style=style)
|
|
111
|
+
each item, index in items
|
|
112
|
+
React.Fragment(key=index)
|
|
113
|
+
- const { type, value, selected, disabled, ...itemProps } = item
|
|
114
|
+
if type === 'page'
|
|
115
|
+
Div.item(
|
|
116
|
+
variant='highlight'
|
|
117
|
+
shape='circle'
|
|
118
|
+
disabled=disabled
|
|
119
|
+
...itemProps
|
|
120
|
+
)
|
|
121
|
+
Span.page(styleName={ selected })= value + 1
|
|
122
|
+
else if ['first', 'last', 'previous', 'next'].includes(type)
|
|
123
|
+
Div.item(
|
|
124
|
+
variant='highlight'
|
|
125
|
+
shape='circle'
|
|
126
|
+
disabled=disabled
|
|
127
|
+
...itemProps
|
|
128
|
+
)
|
|
129
|
+
Icon.icon(
|
|
130
|
+
styleName={disabled}
|
|
131
|
+
icon=ICONS[type]
|
|
132
|
+
)
|
|
133
|
+
else if ~type.indexOf('ellipsis')
|
|
134
|
+
Div.item
|
|
135
|
+
Span ...
|
|
136
|
+
else if type === 'status'
|
|
137
|
+
Div.status(vAlign='center' row)
|
|
138
|
+
Span= value
|
|
139
|
+
`
|
|
140
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/pagination",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"main": "index.tsx",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@startupjs-ui/core": "^0.1.3",
|
|
12
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/icon": "^0.1.3",
|
|
14
|
+
"@startupjs-ui/span": "^0.1.3"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"startupjs": "*"
|
|
20
|
+
},
|
|
21
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
22
|
+
}
|
package/usePagination.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { useBind } from 'startupjs'
|
|
2
|
+
|
|
3
|
+
export interface UsePaginationProps {
|
|
4
|
+
variant?: 'full' | 'compact'
|
|
5
|
+
page?: number
|
|
6
|
+
$page?: any
|
|
7
|
+
pages?: number
|
|
8
|
+
skip?: number
|
|
9
|
+
$skip?: any
|
|
10
|
+
limit?: number
|
|
11
|
+
$limit?: any
|
|
12
|
+
count?: number
|
|
13
|
+
boundaryCount?: number
|
|
14
|
+
siblingCount?: number
|
|
15
|
+
showFirstButton?: boolean
|
|
16
|
+
showLastButton?: boolean
|
|
17
|
+
showPrevButton?: boolean
|
|
18
|
+
showNextButton?: boolean
|
|
19
|
+
disabled?: boolean
|
|
20
|
+
onChangePage?: (page: number) => void
|
|
21
|
+
onChangeLimit?: (limit: number) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type PaginationItemType =
|
|
25
|
+
| 'page'
|
|
26
|
+
| 'first'
|
|
27
|
+
| 'last'
|
|
28
|
+
| 'previous'
|
|
29
|
+
| 'next'
|
|
30
|
+
| 'status'
|
|
31
|
+
| 'start-ellipsis'
|
|
32
|
+
| 'end-ellipsis'
|
|
33
|
+
|
|
34
|
+
interface PaginationItem {
|
|
35
|
+
onPress?: () => void
|
|
36
|
+
type: PaginationItemType
|
|
37
|
+
value: number | string | null
|
|
38
|
+
selected: boolean
|
|
39
|
+
disabled: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default function usePagination ({
|
|
43
|
+
variant,
|
|
44
|
+
page,
|
|
45
|
+
$page,
|
|
46
|
+
pages,
|
|
47
|
+
skip,
|
|
48
|
+
$skip,
|
|
49
|
+
limit,
|
|
50
|
+
$limit,
|
|
51
|
+
count,
|
|
52
|
+
boundaryCount = 1, // min 1
|
|
53
|
+
siblingCount = 1, // min 0
|
|
54
|
+
showFirstButton = false,
|
|
55
|
+
showLastButton = false,
|
|
56
|
+
showPrevButton = true,
|
|
57
|
+
showNextButton = true,
|
|
58
|
+
disabled,
|
|
59
|
+
onChangePage,
|
|
60
|
+
onChangeLimit
|
|
61
|
+
}: UsePaginationProps): PaginationItem[] {
|
|
62
|
+
({ page, onChangePage } = useBind({ $page, page, onChangePage }) as any)
|
|
63
|
+
;({ skip } = useBind({ $skip, skip }) as any)
|
|
64
|
+
// TODO: Add selectbox to component to change limit
|
|
65
|
+
;({ limit, onChangeLimit } = useBind({ $limit, limit, onChangeLimit }) as any)
|
|
66
|
+
|
|
67
|
+
if (page == null) {
|
|
68
|
+
if (skip != null && limit != null) {
|
|
69
|
+
page = Math.ceil(skip / limit)
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error(
|
|
72
|
+
'[@startupjs/ui] usePagination: page cannot be calculated'
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (pages == null) {
|
|
78
|
+
if (count != null && limit != null) {
|
|
79
|
+
pages = Math.ceil(count / limit)
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'[@startupjs/ui] usePagination: pages cannot be calculated'
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// min 1
|
|
88
|
+
const pagesCount = pages != null && pages > 0 ? pages : 1
|
|
89
|
+
const currentPage = page
|
|
90
|
+
|
|
91
|
+
// Basic list of items to render
|
|
92
|
+
const itemList: Array<PaginationItemType | number> = []
|
|
93
|
+
|
|
94
|
+
if (showFirstButton) itemList.push('first')
|
|
95
|
+
if (showPrevButton) itemList.push('previous')
|
|
96
|
+
|
|
97
|
+
if (variant === 'compact') {
|
|
98
|
+
itemList.push('status')
|
|
99
|
+
} else {
|
|
100
|
+
// this logic was taken from here
|
|
101
|
+
// https://github.com/mui-org/material-ui/blob/master/packages/material-ui-lab/src/Pagination/usePagination.js
|
|
102
|
+
const range = (start: number, end: number) => {
|
|
103
|
+
const length = end - start + 1
|
|
104
|
+
return Array.from({ length }, (_, i) => start + i)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const startPages = range(0, Math.min(boundaryCount, pagesCount) - 1)
|
|
108
|
+
const endPages = range(Math.max(pagesCount - boundaryCount, boundaryCount), pagesCount - 1)
|
|
109
|
+
|
|
110
|
+
const siblingsStart = Math.max(
|
|
111
|
+
Math.min(
|
|
112
|
+
// Natural start
|
|
113
|
+
currentPage - siblingCount,
|
|
114
|
+
// Lower boundary when page is high
|
|
115
|
+
pagesCount - boundaryCount - siblingCount * 2 - 2
|
|
116
|
+
),
|
|
117
|
+
// Greater than startPages
|
|
118
|
+
boundaryCount + 1
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const firstEndPage = endPages[0] ?? Number.NaN
|
|
122
|
+
|
|
123
|
+
const siblingsEnd = Math.min(
|
|
124
|
+
Math.max(
|
|
125
|
+
// Natural end
|
|
126
|
+
currentPage + siblingCount,
|
|
127
|
+
// Upper boundary when page is low
|
|
128
|
+
boundaryCount + siblingCount * 2 + 1
|
|
129
|
+
),
|
|
130
|
+
// Less than endPages
|
|
131
|
+
firstEndPage - 2
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
itemList.push(...startPages)
|
|
135
|
+
|
|
136
|
+
// Start ellipsis
|
|
137
|
+
if (siblingsStart > boundaryCount + 1) {
|
|
138
|
+
itemList.push('start-ellipsis')
|
|
139
|
+
} else if (boundaryCount + 1 < pagesCount - boundaryCount) {
|
|
140
|
+
itemList.push(boundaryCount)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Sibling pages
|
|
144
|
+
itemList.push(...range(siblingsStart, siblingsEnd))
|
|
145
|
+
|
|
146
|
+
// End ellipsis
|
|
147
|
+
if (siblingsEnd < pagesCount - boundaryCount - 2) {
|
|
148
|
+
itemList.push('end-ellipsis')
|
|
149
|
+
} else if (pagesCount - boundaryCount > boundaryCount) {
|
|
150
|
+
itemList.push(pagesCount - boundaryCount - 1)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
itemList.push(...endPages)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (showNextButton) itemList.push('next')
|
|
157
|
+
if (showLastButton) itemList.push('last')
|
|
158
|
+
|
|
159
|
+
// Map the button type to its page number
|
|
160
|
+
const buttonPage = (type: PaginationItemType): number | null => {
|
|
161
|
+
switch (type) {
|
|
162
|
+
case 'first':
|
|
163
|
+
return 0
|
|
164
|
+
case 'previous':
|
|
165
|
+
return currentPage - 1
|
|
166
|
+
case 'next':
|
|
167
|
+
return currentPage + 1
|
|
168
|
+
case 'last':
|
|
169
|
+
return pagesCount - 1
|
|
170
|
+
default:
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Convert the basic item list to pagination item props objects
|
|
176
|
+
const items = itemList.map<PaginationItem>((item) => {
|
|
177
|
+
if (typeof item === 'number') {
|
|
178
|
+
return {
|
|
179
|
+
onPress: () => { onChangePage?.(item) },
|
|
180
|
+
type: 'page',
|
|
181
|
+
value: item,
|
|
182
|
+
selected: item === currentPage,
|
|
183
|
+
disabled: Boolean(disabled)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (item === 'status') {
|
|
188
|
+
return {
|
|
189
|
+
type: item,
|
|
190
|
+
value: `${currentPage + 1} of ${pagesCount}`,
|
|
191
|
+
selected: false,
|
|
192
|
+
disabled: Boolean(disabled)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const value = buttonPage(item)
|
|
197
|
+
const isDisabled: boolean = disabled
|
|
198
|
+
? true
|
|
199
|
+
: (!item.includes('ellipsis') &&
|
|
200
|
+
(item === 'next' || item === 'last' ? currentPage >= pagesCount - 1 : currentPage <= 0))
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
onPress: () => { value !== null && onChangePage?.(value) },
|
|
204
|
+
type: item,
|
|
205
|
+
value,
|
|
206
|
+
selected: false,
|
|
207
|
+
disabled: isDisabled
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
return items
|
|
212
|
+
}
|