@sproutsocial/seeds-react-pagination 0.1.28 → 0.1.31
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +37 -0
- package/dist/esm/index.js +239 -66
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +32 -10
- package/dist/index.d.ts +32 -10
- package/dist/index.js +239 -66
- package/dist/index.js.map +1 -1
- package/dist/pagination.css +99 -0
- package/package.json +10 -9
- package/postcss.config.js +6 -0
- package/src/Pagination.stories.tsx +58 -0
- package/src/Pagination.tsx +17 -210
- package/src/PaginationHybrid.tsx +20 -0
- package/src/PaginationStyled.tsx +128 -0
- package/src/PaginationTailwind.tsx +152 -0
- package/src/PaginationTypes.ts +19 -0
- package/src/__tests__/Pagination.test.tsx +164 -0
- package/src/__tests__/usePaginationModel.test.ts +91 -0
- package/src/css.d.ts +1 -0
- package/src/pagination.css +99 -0
- package/src/usePaginationModel.ts +124 -0
package/src/Pagination.tsx
CHANGED
|
@@ -1,210 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
totalRows: number;
|
|
19
|
-
onPageChange: (pageIndex: number) => void;
|
|
20
|
-
onPageSizeChange: (pageSize: number) => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const Pagination = ({
|
|
24
|
-
paginationConfig,
|
|
25
|
-
currentPageIndex,
|
|
26
|
-
pageSize,
|
|
27
|
-
totalRows,
|
|
28
|
-
onPageChange,
|
|
29
|
-
onPageSizeChange,
|
|
30
|
-
}: PaginationProps) => {
|
|
31
|
-
const { displayType, pageSizeOptions, tableCountFunction } = paginationConfig;
|
|
32
|
-
|
|
33
|
-
// Calculate pagination values
|
|
34
|
-
const totalPages = Math.ceil(totalRows / pageSize);
|
|
35
|
-
const currentPage = currentPageIndex + 1; // Convert 0-based to 1-based for display
|
|
36
|
-
const startRow = totalRows === 0 ? 0 : currentPageIndex * pageSize + 1;
|
|
37
|
-
const endRow = Math.min((currentPageIndex + 1) * pageSize, totalRows);
|
|
38
|
-
|
|
39
|
-
const canGoPrevious = currentPageIndex > 0;
|
|
40
|
-
const canGoNext = currentPageIndex < totalPages - 1;
|
|
41
|
-
|
|
42
|
-
const handlePrevious = () => {
|
|
43
|
-
if (canGoPrevious) {
|
|
44
|
-
onPageChange(currentPageIndex - 1);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const handleNext = () => {
|
|
49
|
-
if (canGoNext) {
|
|
50
|
-
onPageChange(currentPageIndex + 1);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const handlePageSizeChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
|
|
55
|
-
const newPageSize = Number(e.currentTarget.value);
|
|
56
|
-
onPageSizeChange(newPageSize);
|
|
57
|
-
// Reset to first page when page size changes
|
|
58
|
-
onPageChange(0);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Generate page number buttons using web-app-core's algorithm
|
|
62
|
-
const getPageNumbers = () => {
|
|
63
|
-
const pages: (number | string)[] = [];
|
|
64
|
-
const size = paginationConfig.size || "default";
|
|
65
|
-
|
|
66
|
-
// Configuration for how many pages to show around current page
|
|
67
|
-
const NUM_PAGE_LOWER = size === "mini" ? 0 : 1;
|
|
68
|
-
const NUM_PAGE_UPPER = size === "mini" ? 0 : 2;
|
|
69
|
-
|
|
70
|
-
// If number of pages is small enough, show all the page tokens without ellipsis
|
|
71
|
-
if (totalPages <= 5 + NUM_PAGE_LOWER + NUM_PAGE_UPPER) {
|
|
72
|
-
for (let i = 1; i <= totalPages; i++) {
|
|
73
|
-
pages.push(i);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
// Add first page
|
|
77
|
-
pages.push(1);
|
|
78
|
-
|
|
79
|
-
// Add ellipsis at the beginning if needed
|
|
80
|
-
if (currentPage > 3 + NUM_PAGE_LOWER) {
|
|
81
|
-
pages.push("...");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Add pages in the middle
|
|
85
|
-
let start: number;
|
|
86
|
-
let end: number; // inclusive index
|
|
87
|
-
|
|
88
|
-
// If near beginning - Show number of pages before ending ellipsis
|
|
89
|
-
if (currentPage <= 3 + NUM_PAGE_LOWER) {
|
|
90
|
-
start = 2;
|
|
91
|
-
end = 3 + NUM_PAGE_LOWER + NUM_PAGE_UPPER;
|
|
92
|
-
} else if (currentPage > totalPages - (3 + NUM_PAGE_UPPER)) {
|
|
93
|
-
// If near end - Show number of pages after starting ellipsis
|
|
94
|
-
start = totalPages - (2 + NUM_PAGE_LOWER + NUM_PAGE_UPPER);
|
|
95
|
-
end = totalPages - 1;
|
|
96
|
-
} else {
|
|
97
|
-
// If in the middle - Show number of pages between two ellipses
|
|
98
|
-
start = currentPage - NUM_PAGE_LOWER;
|
|
99
|
-
end = currentPage + NUM_PAGE_UPPER;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
for (let i = start; i <= end; i++) {
|
|
103
|
-
pages.push(i);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Add a final ellipsis if necessary
|
|
107
|
-
if (currentPage < totalPages - (2 + NUM_PAGE_UPPER)) {
|
|
108
|
-
pages.push("...");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Add the last page number
|
|
112
|
-
pages.push(totalPages);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return pages;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const pageNumbers = getPageNumbers();
|
|
119
|
-
|
|
120
|
-
if (totalPages <= 1 && !pageSizeOptions) {
|
|
121
|
-
// No pagination needed if only one page and no page size options
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<StyledPaginationContainer>
|
|
127
|
-
<StyledPaginationInfo>
|
|
128
|
-
{tableCountFunction({
|
|
129
|
-
startNum: startRow,
|
|
130
|
-
endNum: endRow,
|
|
131
|
-
totalRowCount: totalRows,
|
|
132
|
-
})}
|
|
133
|
-
</StyledPaginationInfo>
|
|
134
|
-
{/* Page size selector */}
|
|
135
|
-
{pageSizeOptions && displayType === "dropdown" && (
|
|
136
|
-
<StyledPageSizeSelector>
|
|
137
|
-
<Select
|
|
138
|
-
id="page-size-select"
|
|
139
|
-
name="page-size"
|
|
140
|
-
value={String(pageSize)}
|
|
141
|
-
onChange={handlePageSizeChange}
|
|
142
|
-
ariaLabel="Rows per page"
|
|
143
|
-
size="small"
|
|
144
|
-
>
|
|
145
|
-
{pageSizeOptions.map((size) => (
|
|
146
|
-
<option key={size} value={size}>
|
|
147
|
-
{size} per page
|
|
148
|
-
</option>
|
|
149
|
-
))}
|
|
150
|
-
</Select>
|
|
151
|
-
</StyledPageSizeSelector>
|
|
152
|
-
)}
|
|
153
|
-
|
|
154
|
-
{/* TODO: Add inline page size selector for displayType === 'inline' */}
|
|
155
|
-
|
|
156
|
-
{/* Page navigation controls */}
|
|
157
|
-
{totalPages > 1 && (
|
|
158
|
-
<StyledPaginationControls>
|
|
159
|
-
<StyledPageButtons>
|
|
160
|
-
<StyledPaginationButton
|
|
161
|
-
onClick={handlePrevious}
|
|
162
|
-
disabled={!canGoPrevious}
|
|
163
|
-
aria-label="Previous page"
|
|
164
|
-
>
|
|
165
|
-
<Icon name="chevron-left-outline" />
|
|
166
|
-
</StyledPaginationButton>
|
|
167
|
-
|
|
168
|
-
{pageNumbers.map((pageNum, index) => {
|
|
169
|
-
if (pageNum === "...") {
|
|
170
|
-
return (
|
|
171
|
-
<StyledPaginationButton
|
|
172
|
-
key={`ellipsis-${index}`}
|
|
173
|
-
disabled
|
|
174
|
-
aria-label="More pages"
|
|
175
|
-
>
|
|
176
|
-
<Icon name="ellipsis-horizontal-outline" />
|
|
177
|
-
</StyledPaginationButton>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const page = pageNum as number;
|
|
182
|
-
const isCurrentPage = page === currentPage;
|
|
183
|
-
|
|
184
|
-
return (
|
|
185
|
-
<StyledPaginationButton
|
|
186
|
-
key={page}
|
|
187
|
-
onClick={() => onPageChange(page - 1)}
|
|
188
|
-
disabled={isCurrentPage}
|
|
189
|
-
aria-label={`Page ${page}`}
|
|
190
|
-
aria-current={isCurrentPage ? "page" : undefined}
|
|
191
|
-
isSelected={isCurrentPage}
|
|
192
|
-
>
|
|
193
|
-
{page}
|
|
194
|
-
</StyledPaginationButton>
|
|
195
|
-
);
|
|
196
|
-
})}
|
|
197
|
-
|
|
198
|
-
<StyledPaginationButton
|
|
199
|
-
onClick={handleNext}
|
|
200
|
-
disabled={!canGoNext}
|
|
201
|
-
aria-label="Next page"
|
|
202
|
-
>
|
|
203
|
-
<Icon name="chevron-right-outline" />
|
|
204
|
-
</StyledPaginationButton>
|
|
205
|
-
</StyledPageButtons>
|
|
206
|
-
</StyledPaginationControls>
|
|
207
|
-
)}
|
|
208
|
-
</StyledPaginationContainer>
|
|
209
|
-
);
|
|
210
|
-
};
|
|
1
|
+
import { PaginationHybrid } from "./PaginationHybrid";
|
|
2
|
+
import type { PaginationProps } from "./PaginationTypes";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pagination component. Automatically routes between the Tailwind implementation
|
|
6
|
+
* (preferred) and the styled-components implementation (for consumers using
|
|
7
|
+
* system props or styled() extension).
|
|
8
|
+
*
|
|
9
|
+
* Defined as a self-named component (rather than a bare re-export of
|
|
10
|
+
* PaginationHybrid) so the `react-docgen` props-docs generation in the Publish
|
|
11
|
+
* pipeline has a suitable component definition to analyze.
|
|
12
|
+
*/
|
|
13
|
+
export const Pagination = (props: PaginationProps) => (
|
|
14
|
+
<PaginationHybrid {...props} />
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
Pagination.displayName = "Pagination";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Pagination component.
|
|
3
|
+
* Routes between the Tailwind implementation (preferred) and the
|
|
4
|
+
* styled-components implementation (for consumers using system props).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { hasStyledProps } from "@sproutsocial/seeds-react-system-props";
|
|
8
|
+
import { PaginationStyled } from "./PaginationStyled";
|
|
9
|
+
import { PaginationTailwind } from "./PaginationTailwind";
|
|
10
|
+
import type { PaginationProps } from "./PaginationTypes";
|
|
11
|
+
|
|
12
|
+
export const PaginationHybrid = (props: PaginationProps) => {
|
|
13
|
+
// Pagination exposes domain props plus an optional className, none of which
|
|
14
|
+
// are styled-system props, so hasStyledProps is effectively always false; the
|
|
15
|
+
// styled-components branch is retained as a pattern-conformance fallback.
|
|
16
|
+
if (hasStyledProps(props)) {
|
|
17
|
+
return <PaginationStyled {...props} />;
|
|
18
|
+
}
|
|
19
|
+
return <PaginationTailwind {...props} />;
|
|
20
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styled-components implementation of the Pagination component.
|
|
3
|
+
* Composes the styled sub-components from styles.ts. Retained for backward
|
|
4
|
+
* compatibility (system props / styled() extension) via PaginationHybrid.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Select } from "@sproutsocial/seeds-react-select";
|
|
8
|
+
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
9
|
+
import type { PaginationProps } from "./PaginationTypes";
|
|
10
|
+
import { usePaginationModel } from "./usePaginationModel";
|
|
11
|
+
import {
|
|
12
|
+
StyledPaginationContainer,
|
|
13
|
+
StyledPaginationControls,
|
|
14
|
+
StyledPaginationInfo,
|
|
15
|
+
StyledPageSizeSelector,
|
|
16
|
+
StyledPageButtons,
|
|
17
|
+
StyledPaginationButton,
|
|
18
|
+
} from "./styles";
|
|
19
|
+
|
|
20
|
+
export const PaginationStyled = (props: PaginationProps) => {
|
|
21
|
+
const { pageSize, totalRows, onPageChange, className } = props;
|
|
22
|
+
const {
|
|
23
|
+
displayType,
|
|
24
|
+
pageSizeOptions,
|
|
25
|
+
tableCountFunction,
|
|
26
|
+
totalPages,
|
|
27
|
+
currentPage,
|
|
28
|
+
startRow,
|
|
29
|
+
endRow,
|
|
30
|
+
canGoPrevious,
|
|
31
|
+
canGoNext,
|
|
32
|
+
handlePrevious,
|
|
33
|
+
handleNext,
|
|
34
|
+
handlePageSizeChange,
|
|
35
|
+
pageNumbers,
|
|
36
|
+
} = usePaginationModel(props);
|
|
37
|
+
|
|
38
|
+
if (totalPages <= 1 && !pageSizeOptions) {
|
|
39
|
+
// No pagination needed if only one page and no page size options
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<StyledPaginationContainer className={className}>
|
|
45
|
+
<StyledPaginationInfo>
|
|
46
|
+
{tableCountFunction({
|
|
47
|
+
startNum: startRow,
|
|
48
|
+
endNum: endRow,
|
|
49
|
+
totalRowCount: totalRows,
|
|
50
|
+
})}
|
|
51
|
+
</StyledPaginationInfo>
|
|
52
|
+
{/* Page size selector */}
|
|
53
|
+
{pageSizeOptions && displayType === "dropdown" && (
|
|
54
|
+
<StyledPageSizeSelector>
|
|
55
|
+
<Select
|
|
56
|
+
id="page-size-select"
|
|
57
|
+
name="page-size"
|
|
58
|
+
value={String(pageSize)}
|
|
59
|
+
onChange={handlePageSizeChange}
|
|
60
|
+
ariaLabel="Rows per page"
|
|
61
|
+
size="small"
|
|
62
|
+
>
|
|
63
|
+
{pageSizeOptions.map((size) => (
|
|
64
|
+
<option key={size} value={size}>
|
|
65
|
+
{size} per page
|
|
66
|
+
</option>
|
|
67
|
+
))}
|
|
68
|
+
</Select>
|
|
69
|
+
</StyledPageSizeSelector>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
{/* TODO: Add inline page size selector for displayType === 'inline' */}
|
|
73
|
+
|
|
74
|
+
{/* Page navigation controls */}
|
|
75
|
+
{totalPages > 1 && (
|
|
76
|
+
<StyledPaginationControls>
|
|
77
|
+
<StyledPageButtons>
|
|
78
|
+
<StyledPaginationButton
|
|
79
|
+
onClick={handlePrevious}
|
|
80
|
+
disabled={!canGoPrevious}
|
|
81
|
+
aria-label="Previous page"
|
|
82
|
+
>
|
|
83
|
+
<Icon name="chevron-left-outline" />
|
|
84
|
+
</StyledPaginationButton>
|
|
85
|
+
|
|
86
|
+
{pageNumbers.map((pageNum, index) => {
|
|
87
|
+
if (pageNum === "...") {
|
|
88
|
+
return (
|
|
89
|
+
<StyledPaginationButton
|
|
90
|
+
key={`ellipsis-${index}`}
|
|
91
|
+
disabled
|
|
92
|
+
aria-label="More pages"
|
|
93
|
+
>
|
|
94
|
+
<Icon name="ellipsis-horizontal-outline" />
|
|
95
|
+
</StyledPaginationButton>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const page = pageNum as number;
|
|
100
|
+
const isCurrentPage = page === currentPage;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<StyledPaginationButton
|
|
104
|
+
key={page}
|
|
105
|
+
onClick={() => onPageChange(page - 1)}
|
|
106
|
+
disabled={isCurrentPage}
|
|
107
|
+
aria-label={`Page ${page}`}
|
|
108
|
+
aria-current={isCurrentPage ? "page" : undefined}
|
|
109
|
+
isSelected={isCurrentPage}
|
|
110
|
+
>
|
|
111
|
+
{page}
|
|
112
|
+
</StyledPaginationButton>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
|
|
116
|
+
<StyledPaginationButton
|
|
117
|
+
onClick={handleNext}
|
|
118
|
+
disabled={!canGoNext}
|
|
119
|
+
aria-label="Next page"
|
|
120
|
+
>
|
|
121
|
+
<Icon name="chevron-right-outline" />
|
|
122
|
+
</StyledPaginationButton>
|
|
123
|
+
</StyledPageButtons>
|
|
124
|
+
</StyledPaginationControls>
|
|
125
|
+
)}
|
|
126
|
+
</StyledPaginationContainer>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind/CSS implementation of the Pagination component.
|
|
3
|
+
* Uses Seeds pagination CSS classes from pagination.css.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Select } from "@sproutsocial/seeds-react-select";
|
|
7
|
+
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
8
|
+
import { Text } from "@sproutsocial/seeds-react-text";
|
|
9
|
+
import type { PaginationProps } from "./PaginationTypes";
|
|
10
|
+
import { usePaginationModel } from "./usePaginationModel";
|
|
11
|
+
|
|
12
|
+
// Utility for merging class names properly
|
|
13
|
+
function cn(
|
|
14
|
+
...inputs: (string | undefined | null | false | Record<string, boolean>)[]
|
|
15
|
+
): string {
|
|
16
|
+
const classes: string[] = [];
|
|
17
|
+
|
|
18
|
+
for (const input of inputs) {
|
|
19
|
+
if (!input) continue;
|
|
20
|
+
|
|
21
|
+
if (typeof input === "string") {
|
|
22
|
+
classes.push(input);
|
|
23
|
+
} else if (typeof input === "object") {
|
|
24
|
+
for (const [key, value] of Object.entries(input)) {
|
|
25
|
+
if (value) {
|
|
26
|
+
classes.push(key);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return classes.join(" ");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const PaginationTailwind = (props: PaginationProps) => {
|
|
36
|
+
const { pageSize, totalRows, onPageChange, className } = props;
|
|
37
|
+
const {
|
|
38
|
+
displayType,
|
|
39
|
+
pageSizeOptions,
|
|
40
|
+
tableCountFunction,
|
|
41
|
+
totalPages,
|
|
42
|
+
currentPage,
|
|
43
|
+
startRow,
|
|
44
|
+
endRow,
|
|
45
|
+
canGoPrevious,
|
|
46
|
+
canGoNext,
|
|
47
|
+
handlePrevious,
|
|
48
|
+
handleNext,
|
|
49
|
+
handlePageSizeChange,
|
|
50
|
+
pageNumbers,
|
|
51
|
+
} = usePaginationModel(props);
|
|
52
|
+
|
|
53
|
+
if (totalPages <= 1 && !pageSizeOptions) {
|
|
54
|
+
// No pagination needed if only one page and no page size options
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className={cn("seeds-pagination-container", className)}>
|
|
60
|
+
<Text className={cn("seeds-pagination-info")}>
|
|
61
|
+
{tableCountFunction({
|
|
62
|
+
startNum: startRow,
|
|
63
|
+
endNum: endRow,
|
|
64
|
+
totalRowCount: totalRows,
|
|
65
|
+
})}
|
|
66
|
+
</Text>
|
|
67
|
+
{/* Page size selector */}
|
|
68
|
+
{pageSizeOptions && displayType === "dropdown" && (
|
|
69
|
+
<div className={cn("seeds-pagination-page-size-selector")}>
|
|
70
|
+
<Select
|
|
71
|
+
id="page-size-select"
|
|
72
|
+
name="page-size"
|
|
73
|
+
value={String(pageSize)}
|
|
74
|
+
onChange={handlePageSizeChange}
|
|
75
|
+
ariaLabel="Rows per page"
|
|
76
|
+
size="small"
|
|
77
|
+
>
|
|
78
|
+
{pageSizeOptions.map((size) => (
|
|
79
|
+
<option key={size} value={size}>
|
|
80
|
+
{size} per page
|
|
81
|
+
</option>
|
|
82
|
+
))}
|
|
83
|
+
</Select>
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{/* TODO: Add inline page size selector for displayType === 'inline' */}
|
|
88
|
+
|
|
89
|
+
{/* Page navigation controls */}
|
|
90
|
+
{totalPages > 1 && (
|
|
91
|
+
<div className={cn("seeds-pagination-controls")}>
|
|
92
|
+
<div className={cn("seeds-pagination-page-buttons")}>
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
className={cn("seeds-pagination-button")}
|
|
96
|
+
onClick={handlePrevious}
|
|
97
|
+
disabled={!canGoPrevious}
|
|
98
|
+
aria-label="Previous page"
|
|
99
|
+
>
|
|
100
|
+
<Icon name="chevron-left-outline" />
|
|
101
|
+
</button>
|
|
102
|
+
|
|
103
|
+
{pageNumbers.map((pageNum, index) => {
|
|
104
|
+
if (pageNum === "...") {
|
|
105
|
+
return (
|
|
106
|
+
<button
|
|
107
|
+
type="button"
|
|
108
|
+
key={`ellipsis-${index}`}
|
|
109
|
+
className={cn("seeds-pagination-button")}
|
|
110
|
+
disabled
|
|
111
|
+
aria-label="More pages"
|
|
112
|
+
>
|
|
113
|
+
<Icon name="ellipsis-horizontal-outline" />
|
|
114
|
+
</button>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const page = pageNum as number;
|
|
119
|
+
const isCurrentPage = page === currentPage;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<button
|
|
123
|
+
type="button"
|
|
124
|
+
key={page}
|
|
125
|
+
className={cn("seeds-pagination-button", {
|
|
126
|
+
"seeds-pagination-button-selected": isCurrentPage,
|
|
127
|
+
})}
|
|
128
|
+
onClick={() => onPageChange(page - 1)}
|
|
129
|
+
disabled={isCurrentPage}
|
|
130
|
+
aria-label={`Page ${page}`}
|
|
131
|
+
aria-current={isCurrentPage ? "page" : undefined}
|
|
132
|
+
>
|
|
133
|
+
{page}
|
|
134
|
+
</button>
|
|
135
|
+
);
|
|
136
|
+
})}
|
|
137
|
+
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
className={cn("seeds-pagination-button")}
|
|
141
|
+
onClick={handleNext}
|
|
142
|
+
disabled={!canGoNext}
|
|
143
|
+
aria-label="Next page"
|
|
144
|
+
>
|
|
145
|
+
<Icon name="chevron-right-outline" />
|
|
146
|
+
</button>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
};
|
package/src/PaginationTypes.ts
CHANGED
|
@@ -44,6 +44,25 @@ export type TypeAllowedPageSizeOptions =
|
|
|
44
44
|
* />
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
|
+
/**
|
|
48
|
+
* Props for the Pagination component.
|
|
49
|
+
*/
|
|
50
|
+
export interface PaginationProps {
|
|
51
|
+
paginationConfig: TypePaginationConfig;
|
|
52
|
+
currentPageIndex: number;
|
|
53
|
+
pageSize: number;
|
|
54
|
+
totalRows: number;
|
|
55
|
+
onPageChange: (pageIndex: number) => void;
|
|
56
|
+
onPageSizeChange: (pageSize: number) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Optional class name applied to the root container. Lets consumers layer
|
|
59
|
+
* Tailwind utility overrides (e.g. `mx-200`, `p-300`) on top of the component
|
|
60
|
+
* styles. Because `className` is not a styled-system prop, providing it keeps
|
|
61
|
+
* the component on the Tailwind rendering path.
|
|
62
|
+
*/
|
|
63
|
+
className?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
47
66
|
export interface TypePaginationConfig {
|
|
48
67
|
/**
|
|
49
68
|
* Initial number of rows per page.
|