@sproutsocial/seeds-react-pagination 0.1.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.
@@ -0,0 +1,21 @@
1
+ yarn run v1.22.22
2
+ $ tsup --dts
3
+ CLI Building entry: src/index.ts
4
+ CLI Using tsconfig: tsconfig.json
5
+ CLI tsup v8.5.0
6
+ CLI Using tsup config: /home/runner/work/seeds/seeds/seeds-react/seeds-react-pagination/tsup.config.ts
7
+ CLI Target: es2022
8
+ CLI Cleaning output folder
9
+ CJS Build start
10
+ ESM Build start
11
+ CJS dist/index.js 9.42 KB
12
+ CJS dist/index.js.map 15.70 KB
13
+ CJS ⚡️ Build success in 160ms
14
+ ESM dist/esm/index.js 7.07 KB
15
+ ESM dist/esm/index.js.map 15.50 KB
16
+ ESM ⚡️ Build success in 155ms
17
+ DTS Build start
18
+ DTS ⚡️ Build success in 25208ms
19
+ DTS dist/index.d.ts 3.08 KB
20
+ DTS dist/index.d.mts 3.08 KB
21
+ Done in 33.52s.
@@ -0,0 +1,223 @@
1
+ // src/Pagination.tsx
2
+ import "react";
3
+ import { Select } from "@sproutsocial/seeds-react-select";
4
+ import { Icon } from "@sproutsocial/seeds-react-icon";
5
+
6
+ // src/styles.ts
7
+ import styled from "styled-components";
8
+ import { Text } from "@sproutsocial/seeds-react-text";
9
+ var StyledPaginationContainer = styled.div`
10
+ ${(props) => props.theme.typography[200]}
11
+ display: flex;
12
+ align-items: center;
13
+ gap: ${(props) => props.theme.space[300]};
14
+ margin-top: ${(props) => props.theme.space[400]};
15
+ flex-wrap: wrap;
16
+ `;
17
+ var StyledPaginationControls = styled.div`
18
+ display: flex;
19
+ align-items: center;
20
+ margin-left: auto;
21
+ `;
22
+ var StyledPaginationInfo = styled(Text)`
23
+ white-space: nowrap;
24
+ `;
25
+ var StyledPageSizeSelector = styled.div`
26
+ display: flex;
27
+ align-items: center;
28
+ gap: ${(props) => props.theme.space[300]};
29
+ `;
30
+ var StyledPageButtons = styled.div`
31
+ display: inline-flex;
32
+ flex-flow: row wrap;
33
+ background-clip: padding-box;
34
+ border: 1px solid
35
+ ${(props) => props.theme.colors.button.secondary.border.base};
36
+ border-radius: ${(props) => props.theme.radii.outer};
37
+ padding: ${(props) => props.theme.space[100]};
38
+ background: transparent;
39
+ `;
40
+ var StyledPaginationButton = styled.button`
41
+ min-width: calc(${(props) => props.theme.space[500]} + 4px);
42
+ min-height: calc(${(props) => props.theme.space[500]} + 2px);
43
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
44
+ line-height: ${(props) => props.theme.space[400]};
45
+ font-weight: ${(props) => props.theme.fontWeights.semibold};
46
+ background-color: ${(props) => props.isSelected ? props.theme.colors.button.secondary.background.active : "transparent"};
47
+ color: ${(props) => props.isSelected ? props.theme.colors.text.inverse : props.theme.colors.text.body};
48
+ border: none;
49
+ border-radius: ${(props) => props.theme.radii.inner};
50
+ cursor: pointer;
51
+ display: inline-flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+
55
+ &:hover:not(:disabled) {
56
+ color: ${(props) => props.isSelected ? props.theme.colors.text.inverse : void 0};
57
+ background-color: ${(props) => props.isSelected ? props.theme.colors.button.secondary.background.active : props.theme.colors.listItem.background.hover};
58
+ }
59
+
60
+ &:disabled {
61
+ cursor: default;
62
+ opacity: ${(props) => props.isSelected ? 1 : 0.5};
63
+ }
64
+ `;
65
+
66
+ // src/Pagination.tsx
67
+ import { jsx, jsxs } from "react/jsx-runtime";
68
+ var Pagination = ({
69
+ paginationConfig,
70
+ currentPageIndex,
71
+ pageSize,
72
+ totalRows,
73
+ onPageChange,
74
+ onPageSizeChange
75
+ }) => {
76
+ const { displayType, pageSizeOptions, tableCountFunction } = paginationConfig;
77
+ const totalPages = Math.ceil(totalRows / pageSize);
78
+ const currentPage = currentPageIndex + 1;
79
+ const startRow = totalRows === 0 ? 0 : currentPageIndex * pageSize + 1;
80
+ const endRow = Math.min((currentPageIndex + 1) * pageSize, totalRows);
81
+ const canGoPrevious = currentPageIndex > 0;
82
+ const canGoNext = currentPageIndex < totalPages - 1;
83
+ const handlePrevious = () => {
84
+ if (canGoPrevious) {
85
+ onPageChange(currentPageIndex - 1);
86
+ }
87
+ };
88
+ const handleNext = () => {
89
+ if (canGoNext) {
90
+ onPageChange(currentPageIndex + 1);
91
+ }
92
+ };
93
+ const handlePageSizeChange = (e) => {
94
+ const newPageSize = Number(e.currentTarget.value);
95
+ onPageSizeChange(newPageSize);
96
+ onPageChange(0);
97
+ };
98
+ const getPageNumbers = () => {
99
+ const pages = [];
100
+ const size = paginationConfig.size || "default";
101
+ const NUM_PAGE_LOWER = size === "mini" ? 0 : 1;
102
+ const NUM_PAGE_UPPER = size === "mini" ? 0 : 2;
103
+ if (totalPages <= 5 + NUM_PAGE_LOWER + NUM_PAGE_UPPER) {
104
+ for (let i = 1; i <= totalPages; i++) {
105
+ pages.push(i);
106
+ }
107
+ } else {
108
+ pages.push(1);
109
+ if (currentPage > 3 + NUM_PAGE_LOWER) {
110
+ pages.push("...");
111
+ }
112
+ let start;
113
+ let end;
114
+ if (currentPage <= 3 + NUM_PAGE_LOWER) {
115
+ start = 2;
116
+ end = 3 + NUM_PAGE_LOWER + NUM_PAGE_UPPER;
117
+ } else if (currentPage > totalPages - (3 + NUM_PAGE_UPPER)) {
118
+ start = totalPages - (2 + NUM_PAGE_LOWER + NUM_PAGE_UPPER);
119
+ end = totalPages - 1;
120
+ } else {
121
+ start = currentPage - NUM_PAGE_LOWER;
122
+ end = currentPage + NUM_PAGE_UPPER;
123
+ }
124
+ for (let i = start; i <= end; i++) {
125
+ pages.push(i);
126
+ }
127
+ if (currentPage < totalPages - (2 + NUM_PAGE_UPPER)) {
128
+ pages.push("...");
129
+ }
130
+ pages.push(totalPages);
131
+ }
132
+ return pages;
133
+ };
134
+ const pageNumbers = getPageNumbers();
135
+ if (totalPages <= 1 && !pageSizeOptions) {
136
+ return null;
137
+ }
138
+ return /* @__PURE__ */ jsxs(StyledPaginationContainer, { children: [
139
+ /* @__PURE__ */ jsx(StyledPaginationInfo, { children: tableCountFunction({
140
+ startNum: startRow,
141
+ endNum: endRow,
142
+ totalRowCount: totalRows
143
+ }) }),
144
+ pageSizeOptions && displayType === "dropdown" && /* @__PURE__ */ jsx(StyledPageSizeSelector, { children: /* @__PURE__ */ jsx(
145
+ Select,
146
+ {
147
+ id: "page-size-select",
148
+ name: "page-size",
149
+ value: String(pageSize),
150
+ onChange: handlePageSizeChange,
151
+ ariaLabel: "Rows per page",
152
+ size: "small",
153
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxs("option", { value: size, children: [
154
+ size,
155
+ " per page"
156
+ ] }, size))
157
+ }
158
+ ) }),
159
+ totalPages > 1 && /* @__PURE__ */ jsx(StyledPaginationControls, { children: /* @__PURE__ */ jsxs(StyledPageButtons, { children: [
160
+ /* @__PURE__ */ jsx(
161
+ StyledPaginationButton,
162
+ {
163
+ onClick: handlePrevious,
164
+ disabled: !canGoPrevious,
165
+ "aria-label": "Previous page",
166
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left-outline" })
167
+ }
168
+ ),
169
+ pageNumbers.map((pageNum, index) => {
170
+ if (pageNum === "...") {
171
+ return /* @__PURE__ */ jsx(
172
+ StyledPaginationButton,
173
+ {
174
+ disabled: true,
175
+ "aria-label": "More pages",
176
+ children: /* @__PURE__ */ jsx(Icon, { name: "ellipsis-horizontal-outline" })
177
+ },
178
+ `ellipsis-${index}`
179
+ );
180
+ }
181
+ const page = pageNum;
182
+ const isCurrentPage = page === currentPage;
183
+ return /* @__PURE__ */ jsx(
184
+ StyledPaginationButton,
185
+ {
186
+ onClick: () => onPageChange(page - 1),
187
+ disabled: isCurrentPage,
188
+ "aria-label": `Page ${page}`,
189
+ "aria-current": isCurrentPage ? "page" : void 0,
190
+ isSelected: isCurrentPage,
191
+ children: page
192
+ },
193
+ page
194
+ );
195
+ }),
196
+ /* @__PURE__ */ jsx(
197
+ StyledPaginationButton,
198
+ {
199
+ onClick: handleNext,
200
+ disabled: !canGoNext,
201
+ "aria-label": "Next page",
202
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right-outline" })
203
+ }
204
+ )
205
+ ] }) })
206
+ ] });
207
+ };
208
+
209
+ // src/PaginationTypes.ts
210
+ import "react";
211
+ var PAGE_SIZE_OPTIONS_STANDARD = [10, 25, 50, 100];
212
+ var PAGE_SIZE_OPTIONS_COMPACT = [5, 10, 20];
213
+ var PAGE_SIZE_OPTIONS_LARGE = [50, 100, 200, 500];
214
+ var PAGE_SIZE_OPTIONS = {
215
+ STANDARD: PAGE_SIZE_OPTIONS_STANDARD,
216
+ COMPACT: PAGE_SIZE_OPTIONS_COMPACT,
217
+ LARGE: PAGE_SIZE_OPTIONS_LARGE
218
+ };
219
+ export {
220
+ PAGE_SIZE_OPTIONS,
221
+ Pagination
222
+ };
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/Pagination.tsx","../../src/styles.ts","../../src/PaginationTypes.ts"],"sourcesContent":["import * as React from \"react\";\nimport { Select } from \"@sproutsocial/seeds-react-select\";\nimport { Icon } from \"@sproutsocial/seeds-react-icon\";\nimport type { TypePaginationConfig } from \"./PaginationTypes\";\nimport {\n StyledPaginationContainer,\n StyledPaginationControls,\n StyledPaginationInfo,\n StyledPageSizeSelector,\n StyledPageButtons,\n StyledPaginationButton,\n} from \"./styles\";\n\ninterface PaginationProps {\n paginationConfig: TypePaginationConfig;\n currentPageIndex: number;\n pageSize: number;\n totalRows: number;\n onPageChange: (pageIndex: number) => void;\n onPageSizeChange: (pageSize: number) => void;\n}\n\nexport const Pagination = ({\n paginationConfig,\n currentPageIndex,\n pageSize,\n totalRows,\n onPageChange,\n onPageSizeChange,\n}: PaginationProps) => {\n const { displayType, pageSizeOptions, tableCountFunction } = paginationConfig;\n\n // Calculate pagination values\n const totalPages = Math.ceil(totalRows / pageSize);\n const currentPage = currentPageIndex + 1; // Convert 0-based to 1-based for display\n const startRow = totalRows === 0 ? 0 : currentPageIndex * pageSize + 1;\n const endRow = Math.min((currentPageIndex + 1) * pageSize, totalRows);\n\n const canGoPrevious = currentPageIndex > 0;\n const canGoNext = currentPageIndex < totalPages - 1;\n\n const handlePrevious = () => {\n if (canGoPrevious) {\n onPageChange(currentPageIndex - 1);\n }\n };\n\n const handleNext = () => {\n if (canGoNext) {\n onPageChange(currentPageIndex + 1);\n }\n };\n\n const handlePageSizeChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {\n const newPageSize = Number(e.currentTarget.value);\n onPageSizeChange(newPageSize);\n // Reset to first page when page size changes\n onPageChange(0);\n };\n\n // Generate page number buttons using web-app-core's algorithm\n const getPageNumbers = () => {\n const pages: (number | string)[] = [];\n const size = paginationConfig.size || \"default\";\n\n // Configuration for how many pages to show around current page\n const NUM_PAGE_LOWER = size === \"mini\" ? 0 : 1;\n const NUM_PAGE_UPPER = size === \"mini\" ? 0 : 2;\n\n // If number of pages is small enough, show all the page tokens without ellipsis\n if (totalPages <= 5 + NUM_PAGE_LOWER + NUM_PAGE_UPPER) {\n for (let i = 1; i <= totalPages; i++) {\n pages.push(i);\n }\n } else {\n // Add first page\n pages.push(1);\n\n // Add ellipsis at the beginning if needed\n if (currentPage > 3 + NUM_PAGE_LOWER) {\n pages.push(\"...\");\n }\n\n // Add pages in the middle\n let start: number;\n let end: number; // inclusive index\n\n // If near beginning - Show number of pages before ending ellipsis\n if (currentPage <= 3 + NUM_PAGE_LOWER) {\n start = 2;\n end = 3 + NUM_PAGE_LOWER + NUM_PAGE_UPPER;\n } else if (currentPage > totalPages - (3 + NUM_PAGE_UPPER)) {\n // If near end - Show number of pages after starting ellipsis\n start = totalPages - (2 + NUM_PAGE_LOWER + NUM_PAGE_UPPER);\n end = totalPages - 1;\n } else {\n // If in the middle - Show number of pages between two ellipses\n start = currentPage - NUM_PAGE_LOWER;\n end = currentPage + NUM_PAGE_UPPER;\n }\n\n for (let i = start; i <= end; i++) {\n pages.push(i);\n }\n\n // Add a final ellipsis if necessary\n if (currentPage < totalPages - (2 + NUM_PAGE_UPPER)) {\n pages.push(\"...\");\n }\n\n // Add the last page number\n pages.push(totalPages);\n }\n\n return pages;\n };\n\n const pageNumbers = getPageNumbers();\n\n if (totalPages <= 1 && !pageSizeOptions) {\n // No pagination needed if only one page and no page size options\n return null;\n }\n\n return (\n <StyledPaginationContainer>\n <StyledPaginationInfo>\n {tableCountFunction({\n startNum: startRow,\n endNum: endRow,\n totalRowCount: totalRows,\n })}\n </StyledPaginationInfo>\n {/* Page size selector */}\n {pageSizeOptions && displayType === \"dropdown\" && (\n <StyledPageSizeSelector>\n <Select\n id=\"page-size-select\"\n name=\"page-size\"\n value={String(pageSize)}\n onChange={handlePageSizeChange}\n ariaLabel=\"Rows per page\"\n size=\"small\"\n >\n {pageSizeOptions.map((size) => (\n <option key={size} value={size}>\n {size} per page\n </option>\n ))}\n </Select>\n </StyledPageSizeSelector>\n )}\n\n {/* TODO: Add inline page size selector for displayType === 'inline' */}\n\n {/* Page navigation controls */}\n {totalPages > 1 && (\n <StyledPaginationControls>\n <StyledPageButtons>\n <StyledPaginationButton\n onClick={handlePrevious}\n disabled={!canGoPrevious}\n aria-label=\"Previous page\"\n >\n <Icon name=\"chevron-left-outline\" />\n </StyledPaginationButton>\n\n {pageNumbers.map((pageNum, index) => {\n if (pageNum === \"...\") {\n return (\n <StyledPaginationButton\n key={`ellipsis-${index}`}\n disabled\n aria-label=\"More pages\"\n >\n <Icon name=\"ellipsis-horizontal-outline\" />\n </StyledPaginationButton>\n );\n }\n\n const page = pageNum as number;\n const isCurrentPage = page === currentPage;\n\n return (\n <StyledPaginationButton\n key={page}\n onClick={() => onPageChange(page - 1)}\n disabled={isCurrentPage}\n aria-label={`Page ${page}`}\n aria-current={isCurrentPage ? \"page\" : undefined}\n isSelected={isCurrentPage}\n >\n {page}\n </StyledPaginationButton>\n );\n })}\n\n <StyledPaginationButton\n onClick={handleNext}\n disabled={!canGoNext}\n aria-label=\"Next page\"\n >\n <Icon name=\"chevron-right-outline\" />\n </StyledPaginationButton>\n </StyledPageButtons>\n </StyledPaginationControls>\n )}\n </StyledPaginationContainer>\n );\n};\n","import styled from \"styled-components\";\nimport { Text } from \"@sproutsocial/seeds-react-text\";\n\n// Pagination Styles\nexport const StyledPaginationContainer = styled.div`\n ${(props) => props.theme.typography[200]}\n display: flex;\n align-items: center;\n gap: ${(props) => props.theme.space[300]};\n margin-top: ${(props) => props.theme.space[400]};\n flex-wrap: wrap;\n`;\n\nexport const StyledPaginationControls = styled.div`\n display: flex;\n align-items: center;\n margin-left: auto;\n`;\n\nexport const StyledPaginationInfo = styled(Text)`\n white-space: nowrap;\n`;\n\nexport const StyledPageSizeSelector = styled.div`\n display: flex;\n align-items: center;\n gap: ${(props) => props.theme.space[300]};\n`;\n\nexport const StyledPageButtons = styled.div`\n display: inline-flex;\n flex-flow: row wrap;\n background-clip: padding-box;\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n background: transparent;\n`;\n\nexport const StyledPaginationButton = styled.button<{ isSelected?: boolean }>`\n min-width: calc(${(props) => props.theme.space[500]} + 4px);\n min-height: calc(${(props) => props.theme.space[500]} + 2px);\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n line-height: ${(props) => props.theme.space[400]};\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n background-color: ${(props) =>\n props.isSelected\n ? props.theme.colors.button.secondary.background.active\n : \"transparent\"};\n color: ${(props) =>\n props.isSelected\n ? props.theme.colors.text.inverse\n : props.theme.colors.text.body};\n border: none;\n border-radius: ${(props) => props.theme.radii.inner};\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &:hover:not(:disabled) {\n color: ${(props) =>\n props.isSelected ? props.theme.colors.text.inverse : undefined};\n background-color: ${(props) =>\n props.isSelected\n ? props.theme.colors.button.secondary.background.active\n : props.theme.colors.listItem.background.hover};\n }\n\n &:disabled {\n cursor: default;\n opacity: ${(props) => (props.isSelected ? 1 : 0.5)};\n }\n`;\n","import * as React from \"react\";\n\n/**\n * Standard page size option sets for pagination.\n * Export these constants to maintain consistency across tables.\n */\nconst PAGE_SIZE_OPTIONS_STANDARD = [10, 25, 50, 100] as const;\nconst PAGE_SIZE_OPTIONS_COMPACT = [5, 10, 20] as const;\nconst PAGE_SIZE_OPTIONS_LARGE = [50, 100, 200, 500] as const;\n\nexport const PAGE_SIZE_OPTIONS = {\n STANDARD: PAGE_SIZE_OPTIONS_STANDARD,\n COMPACT: PAGE_SIZE_OPTIONS_COMPACT,\n LARGE: PAGE_SIZE_OPTIONS_LARGE,\n};\n\n/**\n * Allowed page size option sets.\n * Use one of the predefined PAGE_SIZE_OPTIONS for consistency.\n */\nexport type TypeAllowedPageSizeOptions =\n | typeof PAGE_SIZE_OPTIONS_STANDARD\n | typeof PAGE_SIZE_OPTIONS_COMPACT\n | typeof PAGE_SIZE_OPTIONS_LARGE;\n\n/**\n * Configuration for pagination controls with page navigation and size selection.\n *\n * @example\n * ```tsx\n * <Pagination\n * paginationConfig={{\n * initialPageSize: 25,\n * displayType: 'dropdown',\n * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD,\n * tableCountFunction: ({ startNum, endNum, totalRowCount }) =>\n * `${startNum}-${endNum} of ${totalRowCount}`\n * }}\n * currentPageIndex={0}\n * pageSize={25}\n * totalRows={100}\n * onPageChange={(index) => console.log('Page:', index)}\n * onPageSizeChange={(size) => console.log('Page size:', size)}\n * />\n * ```\n */\nexport interface TypePaginationConfig {\n /**\n * Initial number of rows per page.\n * @default 10\n */\n initialPageSize: number;\n /**\n * Function that returns the intl for displaying the table counts underneath a DataTable\n */\n tableCountFunction: (args: {\n startNum: number;\n endNum: number;\n totalRowCount: number;\n }) => React.ReactNode;\n /**\n * Display mode for page size selector.\n * - 'dropdown': Shows page size selector as a dropdown\n * Only used when pageSizeOptions is provided.\n * @default 'dropdown'\n */\n displayType?: \"dropdown\";\n /**\n * Optional array of page size options for the selector.\n * If not provided, page size selection will not be available.\n * Use PAGE_SIZE_OPTIONS constants for consistency.\n *\n * @example\n * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD // [10, 25, 50, 100]\n */\n pageSizeOptions?: TypeAllowedPageSizeOptions;\n /**\n * Size variant for page number display.\n * - 'default': Shows current page ±1-2 pages (more visible pages)\n * - 'mini': Shows only current page (fewer visible pages)\n * @default 'default'\n */\n size?: \"default\" | \"mini\";\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,91 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * Standard page size option sets for pagination.
6
+ * Export these constants to maintain consistency across tables.
7
+ */
8
+ declare const PAGE_SIZE_OPTIONS_STANDARD: readonly [10, 25, 50, 100];
9
+ declare const PAGE_SIZE_OPTIONS_COMPACT: readonly [5, 10, 20];
10
+ declare const PAGE_SIZE_OPTIONS_LARGE: readonly [50, 100, 200, 500];
11
+ declare const PAGE_SIZE_OPTIONS: {
12
+ STANDARD: readonly [10, 25, 50, 100];
13
+ COMPACT: readonly [5, 10, 20];
14
+ LARGE: readonly [50, 100, 200, 500];
15
+ };
16
+ /**
17
+ * Allowed page size option sets.
18
+ * Use one of the predefined PAGE_SIZE_OPTIONS for consistency.
19
+ */
20
+ type TypeAllowedPageSizeOptions = typeof PAGE_SIZE_OPTIONS_STANDARD | typeof PAGE_SIZE_OPTIONS_COMPACT | typeof PAGE_SIZE_OPTIONS_LARGE;
21
+ /**
22
+ * Configuration for pagination controls with page navigation and size selection.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <Pagination
27
+ * paginationConfig={{
28
+ * initialPageSize: 25,
29
+ * displayType: 'dropdown',
30
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD,
31
+ * tableCountFunction: ({ startNum, endNum, totalRowCount }) =>
32
+ * `${startNum}-${endNum} of ${totalRowCount}`
33
+ * }}
34
+ * currentPageIndex={0}
35
+ * pageSize={25}
36
+ * totalRows={100}
37
+ * onPageChange={(index) => console.log('Page:', index)}
38
+ * onPageSizeChange={(size) => console.log('Page size:', size)}
39
+ * />
40
+ * ```
41
+ */
42
+ interface TypePaginationConfig {
43
+ /**
44
+ * Initial number of rows per page.
45
+ * @default 10
46
+ */
47
+ initialPageSize: number;
48
+ /**
49
+ * Function that returns the intl for displaying the table counts underneath a DataTable
50
+ */
51
+ tableCountFunction: (args: {
52
+ startNum: number;
53
+ endNum: number;
54
+ totalRowCount: number;
55
+ }) => React.ReactNode;
56
+ /**
57
+ * Display mode for page size selector.
58
+ * - 'dropdown': Shows page size selector as a dropdown
59
+ * Only used when pageSizeOptions is provided.
60
+ * @default 'dropdown'
61
+ */
62
+ displayType?: "dropdown";
63
+ /**
64
+ * Optional array of page size options for the selector.
65
+ * If not provided, page size selection will not be available.
66
+ * Use PAGE_SIZE_OPTIONS constants for consistency.
67
+ *
68
+ * @example
69
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD // [10, 25, 50, 100]
70
+ */
71
+ pageSizeOptions?: TypeAllowedPageSizeOptions;
72
+ /**
73
+ * Size variant for page number display.
74
+ * - 'default': Shows current page ±1-2 pages (more visible pages)
75
+ * - 'mini': Shows only current page (fewer visible pages)
76
+ * @default 'default'
77
+ */
78
+ size?: "default" | "mini";
79
+ }
80
+
81
+ interface PaginationProps {
82
+ paginationConfig: TypePaginationConfig;
83
+ currentPageIndex: number;
84
+ pageSize: number;
85
+ totalRows: number;
86
+ onPageChange: (pageIndex: number) => void;
87
+ onPageSizeChange: (pageSize: number) => void;
88
+ }
89
+ declare const Pagination: ({ paginationConfig, currentPageIndex, pageSize, totalRows, onPageChange, onPageSizeChange, }: PaginationProps) => react_jsx_runtime.JSX.Element | null;
90
+
91
+ export { PAGE_SIZE_OPTIONS, Pagination, type TypeAllowedPageSizeOptions, type TypePaginationConfig };
@@ -0,0 +1,91 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * Standard page size option sets for pagination.
6
+ * Export these constants to maintain consistency across tables.
7
+ */
8
+ declare const PAGE_SIZE_OPTIONS_STANDARD: readonly [10, 25, 50, 100];
9
+ declare const PAGE_SIZE_OPTIONS_COMPACT: readonly [5, 10, 20];
10
+ declare const PAGE_SIZE_OPTIONS_LARGE: readonly [50, 100, 200, 500];
11
+ declare const PAGE_SIZE_OPTIONS: {
12
+ STANDARD: readonly [10, 25, 50, 100];
13
+ COMPACT: readonly [5, 10, 20];
14
+ LARGE: readonly [50, 100, 200, 500];
15
+ };
16
+ /**
17
+ * Allowed page size option sets.
18
+ * Use one of the predefined PAGE_SIZE_OPTIONS for consistency.
19
+ */
20
+ type TypeAllowedPageSizeOptions = typeof PAGE_SIZE_OPTIONS_STANDARD | typeof PAGE_SIZE_OPTIONS_COMPACT | typeof PAGE_SIZE_OPTIONS_LARGE;
21
+ /**
22
+ * Configuration for pagination controls with page navigation and size selection.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <Pagination
27
+ * paginationConfig={{
28
+ * initialPageSize: 25,
29
+ * displayType: 'dropdown',
30
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD,
31
+ * tableCountFunction: ({ startNum, endNum, totalRowCount }) =>
32
+ * `${startNum}-${endNum} of ${totalRowCount}`
33
+ * }}
34
+ * currentPageIndex={0}
35
+ * pageSize={25}
36
+ * totalRows={100}
37
+ * onPageChange={(index) => console.log('Page:', index)}
38
+ * onPageSizeChange={(size) => console.log('Page size:', size)}
39
+ * />
40
+ * ```
41
+ */
42
+ interface TypePaginationConfig {
43
+ /**
44
+ * Initial number of rows per page.
45
+ * @default 10
46
+ */
47
+ initialPageSize: number;
48
+ /**
49
+ * Function that returns the intl for displaying the table counts underneath a DataTable
50
+ */
51
+ tableCountFunction: (args: {
52
+ startNum: number;
53
+ endNum: number;
54
+ totalRowCount: number;
55
+ }) => React.ReactNode;
56
+ /**
57
+ * Display mode for page size selector.
58
+ * - 'dropdown': Shows page size selector as a dropdown
59
+ * Only used when pageSizeOptions is provided.
60
+ * @default 'dropdown'
61
+ */
62
+ displayType?: "dropdown";
63
+ /**
64
+ * Optional array of page size options for the selector.
65
+ * If not provided, page size selection will not be available.
66
+ * Use PAGE_SIZE_OPTIONS constants for consistency.
67
+ *
68
+ * @example
69
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD // [10, 25, 50, 100]
70
+ */
71
+ pageSizeOptions?: TypeAllowedPageSizeOptions;
72
+ /**
73
+ * Size variant for page number display.
74
+ * - 'default': Shows current page ±1-2 pages (more visible pages)
75
+ * - 'mini': Shows only current page (fewer visible pages)
76
+ * @default 'default'
77
+ */
78
+ size?: "default" | "mini";
79
+ }
80
+
81
+ interface PaginationProps {
82
+ paginationConfig: TypePaginationConfig;
83
+ currentPageIndex: number;
84
+ pageSize: number;
85
+ totalRows: number;
86
+ onPageChange: (pageIndex: number) => void;
87
+ onPageSizeChange: (pageSize: number) => void;
88
+ }
89
+ declare const Pagination: ({ paginationConfig, currentPageIndex, pageSize, totalRows, onPageChange, onPageSizeChange, }: PaginationProps) => react_jsx_runtime.JSX.Element | null;
90
+
91
+ export { PAGE_SIZE_OPTIONS, Pagination, type TypeAllowedPageSizeOptions, type TypePaginationConfig };
package/dist/index.js ADDED
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ PAGE_SIZE_OPTIONS: () => PAGE_SIZE_OPTIONS,
34
+ Pagination: () => Pagination
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/Pagination.tsx
39
+ var React = require("react");
40
+ var import_seeds_react_select = require("@sproutsocial/seeds-react-select");
41
+ var import_seeds_react_icon = require("@sproutsocial/seeds-react-icon");
42
+
43
+ // src/styles.ts
44
+ var import_styled_components = __toESM(require("styled-components"));
45
+ var import_seeds_react_text = require("@sproutsocial/seeds-react-text");
46
+ var StyledPaginationContainer = import_styled_components.default.div`
47
+ ${(props) => props.theme.typography[200]}
48
+ display: flex;
49
+ align-items: center;
50
+ gap: ${(props) => props.theme.space[300]};
51
+ margin-top: ${(props) => props.theme.space[400]};
52
+ flex-wrap: wrap;
53
+ `;
54
+ var StyledPaginationControls = import_styled_components.default.div`
55
+ display: flex;
56
+ align-items: center;
57
+ margin-left: auto;
58
+ `;
59
+ var StyledPaginationInfo = (0, import_styled_components.default)(import_seeds_react_text.Text)`
60
+ white-space: nowrap;
61
+ `;
62
+ var StyledPageSizeSelector = import_styled_components.default.div`
63
+ display: flex;
64
+ align-items: center;
65
+ gap: ${(props) => props.theme.space[300]};
66
+ `;
67
+ var StyledPageButtons = import_styled_components.default.div`
68
+ display: inline-flex;
69
+ flex-flow: row wrap;
70
+ background-clip: padding-box;
71
+ border: 1px solid
72
+ ${(props) => props.theme.colors.button.secondary.border.base};
73
+ border-radius: ${(props) => props.theme.radii.outer};
74
+ padding: ${(props) => props.theme.space[100]};
75
+ background: transparent;
76
+ `;
77
+ var StyledPaginationButton = import_styled_components.default.button`
78
+ min-width: calc(${(props) => props.theme.space[500]} + 4px);
79
+ min-height: calc(${(props) => props.theme.space[500]} + 2px);
80
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
81
+ line-height: ${(props) => props.theme.space[400]};
82
+ font-weight: ${(props) => props.theme.fontWeights.semibold};
83
+ background-color: ${(props) => props.isSelected ? props.theme.colors.button.secondary.background.active : "transparent"};
84
+ color: ${(props) => props.isSelected ? props.theme.colors.text.inverse : props.theme.colors.text.body};
85
+ border: none;
86
+ border-radius: ${(props) => props.theme.radii.inner};
87
+ cursor: pointer;
88
+ display: inline-flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+
92
+ &:hover:not(:disabled) {
93
+ color: ${(props) => props.isSelected ? props.theme.colors.text.inverse : void 0};
94
+ background-color: ${(props) => props.isSelected ? props.theme.colors.button.secondary.background.active : props.theme.colors.listItem.background.hover};
95
+ }
96
+
97
+ &:disabled {
98
+ cursor: default;
99
+ opacity: ${(props) => props.isSelected ? 1 : 0.5};
100
+ }
101
+ `;
102
+
103
+ // src/Pagination.tsx
104
+ var import_jsx_runtime = require("react/jsx-runtime");
105
+ var Pagination = ({
106
+ paginationConfig,
107
+ currentPageIndex,
108
+ pageSize,
109
+ totalRows,
110
+ onPageChange,
111
+ onPageSizeChange
112
+ }) => {
113
+ const { displayType, pageSizeOptions, tableCountFunction } = paginationConfig;
114
+ const totalPages = Math.ceil(totalRows / pageSize);
115
+ const currentPage = currentPageIndex + 1;
116
+ const startRow = totalRows === 0 ? 0 : currentPageIndex * pageSize + 1;
117
+ const endRow = Math.min((currentPageIndex + 1) * pageSize, totalRows);
118
+ const canGoPrevious = currentPageIndex > 0;
119
+ const canGoNext = currentPageIndex < totalPages - 1;
120
+ const handlePrevious = () => {
121
+ if (canGoPrevious) {
122
+ onPageChange(currentPageIndex - 1);
123
+ }
124
+ };
125
+ const handleNext = () => {
126
+ if (canGoNext) {
127
+ onPageChange(currentPageIndex + 1);
128
+ }
129
+ };
130
+ const handlePageSizeChange = (e) => {
131
+ const newPageSize = Number(e.currentTarget.value);
132
+ onPageSizeChange(newPageSize);
133
+ onPageChange(0);
134
+ };
135
+ const getPageNumbers = () => {
136
+ const pages = [];
137
+ const size = paginationConfig.size || "default";
138
+ const NUM_PAGE_LOWER = size === "mini" ? 0 : 1;
139
+ const NUM_PAGE_UPPER = size === "mini" ? 0 : 2;
140
+ if (totalPages <= 5 + NUM_PAGE_LOWER + NUM_PAGE_UPPER) {
141
+ for (let i = 1; i <= totalPages; i++) {
142
+ pages.push(i);
143
+ }
144
+ } else {
145
+ pages.push(1);
146
+ if (currentPage > 3 + NUM_PAGE_LOWER) {
147
+ pages.push("...");
148
+ }
149
+ let start;
150
+ let end;
151
+ if (currentPage <= 3 + NUM_PAGE_LOWER) {
152
+ start = 2;
153
+ end = 3 + NUM_PAGE_LOWER + NUM_PAGE_UPPER;
154
+ } else if (currentPage > totalPages - (3 + NUM_PAGE_UPPER)) {
155
+ start = totalPages - (2 + NUM_PAGE_LOWER + NUM_PAGE_UPPER);
156
+ end = totalPages - 1;
157
+ } else {
158
+ start = currentPage - NUM_PAGE_LOWER;
159
+ end = currentPage + NUM_PAGE_UPPER;
160
+ }
161
+ for (let i = start; i <= end; i++) {
162
+ pages.push(i);
163
+ }
164
+ if (currentPage < totalPages - (2 + NUM_PAGE_UPPER)) {
165
+ pages.push("...");
166
+ }
167
+ pages.push(totalPages);
168
+ }
169
+ return pages;
170
+ };
171
+ const pageNumbers = getPageNumbers();
172
+ if (totalPages <= 1 && !pageSizeOptions) {
173
+ return null;
174
+ }
175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StyledPaginationContainer, { children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledPaginationInfo, { children: tableCountFunction({
177
+ startNum: startRow,
178
+ endNum: endRow,
179
+ totalRowCount: totalRows
180
+ }) }),
181
+ pageSizeOptions && displayType === "dropdown" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledPageSizeSelector, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
+ import_seeds_react_select.Select,
183
+ {
184
+ id: "page-size-select",
185
+ name: "page-size",
186
+ value: String(pageSize),
187
+ onChange: handlePageSizeChange,
188
+ ariaLabel: "Rows per page",
189
+ size: "small",
190
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: size, children: [
191
+ size,
192
+ " per page"
193
+ ] }, size))
194
+ }
195
+ ) }),
196
+ totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledPaginationControls, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StyledPageButtons, { children: [
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
198
+ StyledPaginationButton,
199
+ {
200
+ onClick: handlePrevious,
201
+ disabled: !canGoPrevious,
202
+ "aria-label": "Previous page",
203
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_icon.Icon, { name: "chevron-left-outline" })
204
+ }
205
+ ),
206
+ pageNumbers.map((pageNum, index) => {
207
+ if (pageNum === "...") {
208
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
209
+ StyledPaginationButton,
210
+ {
211
+ disabled: true,
212
+ "aria-label": "More pages",
213
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_icon.Icon, { name: "ellipsis-horizontal-outline" })
214
+ },
215
+ `ellipsis-${index}`
216
+ );
217
+ }
218
+ const page = pageNum;
219
+ const isCurrentPage = page === currentPage;
220
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
+ StyledPaginationButton,
222
+ {
223
+ onClick: () => onPageChange(page - 1),
224
+ disabled: isCurrentPage,
225
+ "aria-label": `Page ${page}`,
226
+ "aria-current": isCurrentPage ? "page" : void 0,
227
+ isSelected: isCurrentPage,
228
+ children: page
229
+ },
230
+ page
231
+ );
232
+ }),
233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
234
+ StyledPaginationButton,
235
+ {
236
+ onClick: handleNext,
237
+ disabled: !canGoNext,
238
+ "aria-label": "Next page",
239
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_icon.Icon, { name: "chevron-right-outline" })
240
+ }
241
+ )
242
+ ] }) })
243
+ ] });
244
+ };
245
+
246
+ // src/PaginationTypes.ts
247
+ var React2 = require("react");
248
+ var PAGE_SIZE_OPTIONS_STANDARD = [10, 25, 50, 100];
249
+ var PAGE_SIZE_OPTIONS_COMPACT = [5, 10, 20];
250
+ var PAGE_SIZE_OPTIONS_LARGE = [50, 100, 200, 500];
251
+ var PAGE_SIZE_OPTIONS = {
252
+ STANDARD: PAGE_SIZE_OPTIONS_STANDARD,
253
+ COMPACT: PAGE_SIZE_OPTIONS_COMPACT,
254
+ LARGE: PAGE_SIZE_OPTIONS_LARGE
255
+ };
256
+ // Annotate the CommonJS export names for ESM import in node:
257
+ 0 && (module.exports = {
258
+ PAGE_SIZE_OPTIONS,
259
+ Pagination
260
+ });
261
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/Pagination.tsx","../src/styles.ts","../src/PaginationTypes.ts"],"sourcesContent":["export { Pagination } from \"./Pagination\";\nexport * from \"./PaginationTypes\";\n","import * as React from \"react\";\nimport { Select } from \"@sproutsocial/seeds-react-select\";\nimport { Icon } from \"@sproutsocial/seeds-react-icon\";\nimport type { TypePaginationConfig } from \"./PaginationTypes\";\nimport {\n StyledPaginationContainer,\n StyledPaginationControls,\n StyledPaginationInfo,\n StyledPageSizeSelector,\n StyledPageButtons,\n StyledPaginationButton,\n} from \"./styles\";\n\ninterface PaginationProps {\n paginationConfig: TypePaginationConfig;\n currentPageIndex: number;\n pageSize: number;\n totalRows: number;\n onPageChange: (pageIndex: number) => void;\n onPageSizeChange: (pageSize: number) => void;\n}\n\nexport const Pagination = ({\n paginationConfig,\n currentPageIndex,\n pageSize,\n totalRows,\n onPageChange,\n onPageSizeChange,\n}: PaginationProps) => {\n const { displayType, pageSizeOptions, tableCountFunction } = paginationConfig;\n\n // Calculate pagination values\n const totalPages = Math.ceil(totalRows / pageSize);\n const currentPage = currentPageIndex + 1; // Convert 0-based to 1-based for display\n const startRow = totalRows === 0 ? 0 : currentPageIndex * pageSize + 1;\n const endRow = Math.min((currentPageIndex + 1) * pageSize, totalRows);\n\n const canGoPrevious = currentPageIndex > 0;\n const canGoNext = currentPageIndex < totalPages - 1;\n\n const handlePrevious = () => {\n if (canGoPrevious) {\n onPageChange(currentPageIndex - 1);\n }\n };\n\n const handleNext = () => {\n if (canGoNext) {\n onPageChange(currentPageIndex + 1);\n }\n };\n\n const handlePageSizeChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {\n const newPageSize = Number(e.currentTarget.value);\n onPageSizeChange(newPageSize);\n // Reset to first page when page size changes\n onPageChange(0);\n };\n\n // Generate page number buttons using web-app-core's algorithm\n const getPageNumbers = () => {\n const pages: (number | string)[] = [];\n const size = paginationConfig.size || \"default\";\n\n // Configuration for how many pages to show around current page\n const NUM_PAGE_LOWER = size === \"mini\" ? 0 : 1;\n const NUM_PAGE_UPPER = size === \"mini\" ? 0 : 2;\n\n // If number of pages is small enough, show all the page tokens without ellipsis\n if (totalPages <= 5 + NUM_PAGE_LOWER + NUM_PAGE_UPPER) {\n for (let i = 1; i <= totalPages; i++) {\n pages.push(i);\n }\n } else {\n // Add first page\n pages.push(1);\n\n // Add ellipsis at the beginning if needed\n if (currentPage > 3 + NUM_PAGE_LOWER) {\n pages.push(\"...\");\n }\n\n // Add pages in the middle\n let start: number;\n let end: number; // inclusive index\n\n // If near beginning - Show number of pages before ending ellipsis\n if (currentPage <= 3 + NUM_PAGE_LOWER) {\n start = 2;\n end = 3 + NUM_PAGE_LOWER + NUM_PAGE_UPPER;\n } else if (currentPage > totalPages - (3 + NUM_PAGE_UPPER)) {\n // If near end - Show number of pages after starting ellipsis\n start = totalPages - (2 + NUM_PAGE_LOWER + NUM_PAGE_UPPER);\n end = totalPages - 1;\n } else {\n // If in the middle - Show number of pages between two ellipses\n start = currentPage - NUM_PAGE_LOWER;\n end = currentPage + NUM_PAGE_UPPER;\n }\n\n for (let i = start; i <= end; i++) {\n pages.push(i);\n }\n\n // Add a final ellipsis if necessary\n if (currentPage < totalPages - (2 + NUM_PAGE_UPPER)) {\n pages.push(\"...\");\n }\n\n // Add the last page number\n pages.push(totalPages);\n }\n\n return pages;\n };\n\n const pageNumbers = getPageNumbers();\n\n if (totalPages <= 1 && !pageSizeOptions) {\n // No pagination needed if only one page and no page size options\n return null;\n }\n\n return (\n <StyledPaginationContainer>\n <StyledPaginationInfo>\n {tableCountFunction({\n startNum: startRow,\n endNum: endRow,\n totalRowCount: totalRows,\n })}\n </StyledPaginationInfo>\n {/* Page size selector */}\n {pageSizeOptions && displayType === \"dropdown\" && (\n <StyledPageSizeSelector>\n <Select\n id=\"page-size-select\"\n name=\"page-size\"\n value={String(pageSize)}\n onChange={handlePageSizeChange}\n ariaLabel=\"Rows per page\"\n size=\"small\"\n >\n {pageSizeOptions.map((size) => (\n <option key={size} value={size}>\n {size} per page\n </option>\n ))}\n </Select>\n </StyledPageSizeSelector>\n )}\n\n {/* TODO: Add inline page size selector for displayType === 'inline' */}\n\n {/* Page navigation controls */}\n {totalPages > 1 && (\n <StyledPaginationControls>\n <StyledPageButtons>\n <StyledPaginationButton\n onClick={handlePrevious}\n disabled={!canGoPrevious}\n aria-label=\"Previous page\"\n >\n <Icon name=\"chevron-left-outline\" />\n </StyledPaginationButton>\n\n {pageNumbers.map((pageNum, index) => {\n if (pageNum === \"...\") {\n return (\n <StyledPaginationButton\n key={`ellipsis-${index}`}\n disabled\n aria-label=\"More pages\"\n >\n <Icon name=\"ellipsis-horizontal-outline\" />\n </StyledPaginationButton>\n );\n }\n\n const page = pageNum as number;\n const isCurrentPage = page === currentPage;\n\n return (\n <StyledPaginationButton\n key={page}\n onClick={() => onPageChange(page - 1)}\n disabled={isCurrentPage}\n aria-label={`Page ${page}`}\n aria-current={isCurrentPage ? \"page\" : undefined}\n isSelected={isCurrentPage}\n >\n {page}\n </StyledPaginationButton>\n );\n })}\n\n <StyledPaginationButton\n onClick={handleNext}\n disabled={!canGoNext}\n aria-label=\"Next page\"\n >\n <Icon name=\"chevron-right-outline\" />\n </StyledPaginationButton>\n </StyledPageButtons>\n </StyledPaginationControls>\n )}\n </StyledPaginationContainer>\n );\n};\n","import styled from \"styled-components\";\nimport { Text } from \"@sproutsocial/seeds-react-text\";\n\n// Pagination Styles\nexport const StyledPaginationContainer = styled.div`\n ${(props) => props.theme.typography[200]}\n display: flex;\n align-items: center;\n gap: ${(props) => props.theme.space[300]};\n margin-top: ${(props) => props.theme.space[400]};\n flex-wrap: wrap;\n`;\n\nexport const StyledPaginationControls = styled.div`\n display: flex;\n align-items: center;\n margin-left: auto;\n`;\n\nexport const StyledPaginationInfo = styled(Text)`\n white-space: nowrap;\n`;\n\nexport const StyledPageSizeSelector = styled.div`\n display: flex;\n align-items: center;\n gap: ${(props) => props.theme.space[300]};\n`;\n\nexport const StyledPageButtons = styled.div`\n display: inline-flex;\n flex-flow: row wrap;\n background-clip: padding-box;\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n background: transparent;\n`;\n\nexport const StyledPaginationButton = styled.button<{ isSelected?: boolean }>`\n min-width: calc(${(props) => props.theme.space[500]} + 4px);\n min-height: calc(${(props) => props.theme.space[500]} + 2px);\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n line-height: ${(props) => props.theme.space[400]};\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n background-color: ${(props) =>\n props.isSelected\n ? props.theme.colors.button.secondary.background.active\n : \"transparent\"};\n color: ${(props) =>\n props.isSelected\n ? props.theme.colors.text.inverse\n : props.theme.colors.text.body};\n border: none;\n border-radius: ${(props) => props.theme.radii.inner};\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &:hover:not(:disabled) {\n color: ${(props) =>\n props.isSelected ? props.theme.colors.text.inverse : undefined};\n background-color: ${(props) =>\n props.isSelected\n ? props.theme.colors.button.secondary.background.active\n : props.theme.colors.listItem.background.hover};\n }\n\n &:disabled {\n cursor: default;\n opacity: ${(props) => (props.isSelected ? 1 : 0.5)};\n }\n`;\n","import * as React from \"react\";\n\n/**\n * Standard page size option sets for pagination.\n * Export these constants to maintain consistency across tables.\n */\nconst PAGE_SIZE_OPTIONS_STANDARD = [10, 25, 50, 100] as const;\nconst PAGE_SIZE_OPTIONS_COMPACT = [5, 10, 20] as const;\nconst PAGE_SIZE_OPTIONS_LARGE = [50, 100, 200, 500] as const;\n\nexport const PAGE_SIZE_OPTIONS = {\n STANDARD: PAGE_SIZE_OPTIONS_STANDARD,\n COMPACT: PAGE_SIZE_OPTIONS_COMPACT,\n LARGE: PAGE_SIZE_OPTIONS_LARGE,\n};\n\n/**\n * Allowed page size option sets.\n * Use one of the predefined PAGE_SIZE_OPTIONS for consistency.\n */\nexport type TypeAllowedPageSizeOptions =\n | typeof PAGE_SIZE_OPTIONS_STANDARD\n | typeof PAGE_SIZE_OPTIONS_COMPACT\n | typeof PAGE_SIZE_OPTIONS_LARGE;\n\n/**\n * Configuration for pagination controls with page navigation and size selection.\n *\n * @example\n * ```tsx\n * <Pagination\n * paginationConfig={{\n * initialPageSize: 25,\n * displayType: 'dropdown',\n * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD,\n * tableCountFunction: ({ startNum, endNum, totalRowCount }) =>\n * `${startNum}-${endNum} of ${totalRowCount}`\n * }}\n * currentPageIndex={0}\n * pageSize={25}\n * totalRows={100}\n * onPageChange={(index) => console.log('Page:', index)}\n * onPageSizeChange={(size) => console.log('Page size:', size)}\n * />\n * ```\n */\nexport interface TypePaginationConfig {\n /**\n * Initial number of rows per page.\n * @default 10\n */\n initialPageSize: number;\n /**\n * Function that returns the intl for displaying the table counts underneath a DataTable\n */\n tableCountFunction: (args: {\n startNum: number;\n endNum: number;\n totalRowCount: number;\n }) => React.ReactNode;\n /**\n * Display mode for page size selector.\n * - 'dropdown': Shows page size selector as a dropdown\n * Only used when pageSizeOptions is provided.\n * @default 'dropdown'\n */\n displayType?: \"dropdown\";\n /**\n * Optional array of page size options for the selector.\n * If not provided, page size selection will not be available.\n * Use PAGE_SIZE_OPTIONS constants for consistency.\n *\n * @example\n * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD // [10, 25, 50, 100]\n */\n pageSizeOptions?: TypeAllowedPageSizeOptions;\n /**\n * Size variant for page number display.\n * - 'default': Shows current page ±1-2 pages (more visible pages)\n * - 'mini': Shows only current page (fewer visible pages)\n * @default 'default'\n */\n size?: \"default\" | \"mini\";\n}\n"],"mappings":"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","names":["styled","React"]}
package/jest.config.js ADDED
@@ -0,0 +1,11 @@
1
+ const baseConfig = require("@sproutsocial/seeds-testing");
2
+
3
+ /**
4
+ * @type {import('jest').Config}
5
+ */
6
+ const config = {
7
+ ...baseConfig,
8
+ displayName: "seeds-react-pagination",
9
+ };
10
+
11
+ module.exports = config;
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@sproutsocial/seeds-react-pagination",
3
+ "version": "0.1.0",
4
+ "description": "Seeds React Pagination",
5
+ "author": "Sprout Social, Inc.",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/esm/index.js",
14
+ "require": "./dist/index.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "tsup --dts",
19
+ "build:debug": "tsup --dts --metafile",
20
+ "dev": "tsup --watch --dts",
21
+ "clean": "rm -rf .turbo dist",
22
+ "clean:modules": "rm -rf node_modules",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "jest",
25
+ "test:watch": "jest --watch --coverage=false"
26
+ },
27
+ "dependencies": {
28
+ "@sproutsocial/seeds-react-theme": "^3.5.1",
29
+ "@sproutsocial/seeds-react-system-props": "^3.0.2",
30
+ "@sproutsocial/seeds-react-icon": "^2.2.4",
31
+ "@sproutsocial/seeds-react-select": "^1.1.21",
32
+ "@sproutsocial/seeds-react-text": "^1.4.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "^18.0.0",
36
+ "@types/styled-components": "^5.1.26",
37
+ "@sproutsocial/eslint-config-seeds": "*",
38
+ "react": "^18.0.0",
39
+ "styled-components": "^5.2.3",
40
+ "tsup": "^8.3.4",
41
+ "typescript": "^5.6.2",
42
+ "@sproutsocial/seeds-tsconfig": "*",
43
+ "@sproutsocial/seeds-testing": "*",
44
+ "@sproutsocial/seeds-react-testing-library": "*"
45
+ },
46
+ "peerDependencies": {
47
+ "styled-components": "^5.2.3"
48
+ },
49
+ "engines": {
50
+ "node": ">=18"
51
+ }
52
+ }
@@ -0,0 +1,210 @@
1
+ import * as React from "react";
2
+ import { Select } from "@sproutsocial/seeds-react-select";
3
+ import { Icon } from "@sproutsocial/seeds-react-icon";
4
+ import type { TypePaginationConfig } from "./PaginationTypes";
5
+ import {
6
+ StyledPaginationContainer,
7
+ StyledPaginationControls,
8
+ StyledPaginationInfo,
9
+ StyledPageSizeSelector,
10
+ StyledPageButtons,
11
+ StyledPaginationButton,
12
+ } from "./styles";
13
+
14
+ interface PaginationProps {
15
+ paginationConfig: TypePaginationConfig;
16
+ currentPageIndex: number;
17
+ pageSize: number;
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
+ };
@@ -0,0 +1,84 @@
1
+ import * as React from "react";
2
+
3
+ /**
4
+ * Standard page size option sets for pagination.
5
+ * Export these constants to maintain consistency across tables.
6
+ */
7
+ const PAGE_SIZE_OPTIONS_STANDARD = [10, 25, 50, 100] as const;
8
+ const PAGE_SIZE_OPTIONS_COMPACT = [5, 10, 20] as const;
9
+ const PAGE_SIZE_OPTIONS_LARGE = [50, 100, 200, 500] as const;
10
+
11
+ export const PAGE_SIZE_OPTIONS = {
12
+ STANDARD: PAGE_SIZE_OPTIONS_STANDARD,
13
+ COMPACT: PAGE_SIZE_OPTIONS_COMPACT,
14
+ LARGE: PAGE_SIZE_OPTIONS_LARGE,
15
+ };
16
+
17
+ /**
18
+ * Allowed page size option sets.
19
+ * Use one of the predefined PAGE_SIZE_OPTIONS for consistency.
20
+ */
21
+ export type TypeAllowedPageSizeOptions =
22
+ | typeof PAGE_SIZE_OPTIONS_STANDARD
23
+ | typeof PAGE_SIZE_OPTIONS_COMPACT
24
+ | typeof PAGE_SIZE_OPTIONS_LARGE;
25
+
26
+ /**
27
+ * Configuration for pagination controls with page navigation and size selection.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <Pagination
32
+ * paginationConfig={{
33
+ * initialPageSize: 25,
34
+ * displayType: 'dropdown',
35
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD,
36
+ * tableCountFunction: ({ startNum, endNum, totalRowCount }) =>
37
+ * `${startNum}-${endNum} of ${totalRowCount}`
38
+ * }}
39
+ * currentPageIndex={0}
40
+ * pageSize={25}
41
+ * totalRows={100}
42
+ * onPageChange={(index) => console.log('Page:', index)}
43
+ * onPageSizeChange={(size) => console.log('Page size:', size)}
44
+ * />
45
+ * ```
46
+ */
47
+ export interface TypePaginationConfig {
48
+ /**
49
+ * Initial number of rows per page.
50
+ * @default 10
51
+ */
52
+ initialPageSize: number;
53
+ /**
54
+ * Function that returns the intl for displaying the table counts underneath a DataTable
55
+ */
56
+ tableCountFunction: (args: {
57
+ startNum: number;
58
+ endNum: number;
59
+ totalRowCount: number;
60
+ }) => React.ReactNode;
61
+ /**
62
+ * Display mode for page size selector.
63
+ * - 'dropdown': Shows page size selector as a dropdown
64
+ * Only used when pageSizeOptions is provided.
65
+ * @default 'dropdown'
66
+ */
67
+ displayType?: "dropdown";
68
+ /**
69
+ * Optional array of page size options for the selector.
70
+ * If not provided, page size selection will not be available.
71
+ * Use PAGE_SIZE_OPTIONS constants for consistency.
72
+ *
73
+ * @example
74
+ * pageSizeOptions: PAGE_SIZE_OPTIONS.STANDARD // [10, 25, 50, 100]
75
+ */
76
+ pageSizeOptions?: TypeAllowedPageSizeOptions;
77
+ /**
78
+ * Size variant for page number display.
79
+ * - 'default': Shows current page ±1-2 pages (more visible pages)
80
+ * - 'mini': Shows only current page (fewer visible pages)
81
+ * @default 'default'
82
+ */
83
+ size?: "default" | "mini";
84
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Pagination } from "./Pagination";
2
+ export * from "./PaginationTypes";
package/src/styles.ts ADDED
@@ -0,0 +1,75 @@
1
+ import styled from "styled-components";
2
+ import { Text } from "@sproutsocial/seeds-react-text";
3
+
4
+ // Pagination Styles
5
+ export const StyledPaginationContainer = styled.div`
6
+ ${(props) => props.theme.typography[200]}
7
+ display: flex;
8
+ align-items: center;
9
+ gap: ${(props) => props.theme.space[300]};
10
+ margin-top: ${(props) => props.theme.space[400]};
11
+ flex-wrap: wrap;
12
+ `;
13
+
14
+ export const StyledPaginationControls = styled.div`
15
+ display: flex;
16
+ align-items: center;
17
+ margin-left: auto;
18
+ `;
19
+
20
+ export const StyledPaginationInfo = styled(Text)`
21
+ white-space: nowrap;
22
+ `;
23
+
24
+ export const StyledPageSizeSelector = styled.div`
25
+ display: flex;
26
+ align-items: center;
27
+ gap: ${(props) => props.theme.space[300]};
28
+ `;
29
+
30
+ export const StyledPageButtons = styled.div`
31
+ display: inline-flex;
32
+ flex-flow: row wrap;
33
+ background-clip: padding-box;
34
+ border: 1px solid
35
+ ${(props) => props.theme.colors.button.secondary.border.base};
36
+ border-radius: ${(props) => props.theme.radii.outer};
37
+ padding: ${(props) => props.theme.space[100]};
38
+ background: transparent;
39
+ `;
40
+
41
+ export const StyledPaginationButton = styled.button<{ isSelected?: boolean }>`
42
+ min-width: calc(${(props) => props.theme.space[500]} + 4px);
43
+ min-height: calc(${(props) => props.theme.space[500]} + 2px);
44
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
45
+ line-height: ${(props) => props.theme.space[400]};
46
+ font-weight: ${(props) => props.theme.fontWeights.semibold};
47
+ background-color: ${(props) =>
48
+ props.isSelected
49
+ ? props.theme.colors.button.secondary.background.active
50
+ : "transparent"};
51
+ color: ${(props) =>
52
+ props.isSelected
53
+ ? props.theme.colors.text.inverse
54
+ : props.theme.colors.text.body};
55
+ border: none;
56
+ border-radius: ${(props) => props.theme.radii.inner};
57
+ cursor: pointer;
58
+ display: inline-flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+
62
+ &:hover:not(:disabled) {
63
+ color: ${(props) =>
64
+ props.isSelected ? props.theme.colors.text.inverse : undefined};
65
+ background-color: ${(props) =>
66
+ props.isSelected
67
+ ? props.theme.colors.button.secondary.background.active
68
+ : props.theme.colors.listItem.background.hover};
69
+ }
70
+
71
+ &:disabled {
72
+ cursor: default;
73
+ opacity: ${(props) => (props.isSelected ? 1 : 0.5)};
74
+ }
75
+ `;
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@sproutsocial/seeds-tsconfig/bundler/dom/library-monorepo",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx"
5
+ },
6
+ "include": ["src/**/*"],
7
+ "exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.stories.ts"]
8
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig((options) => ({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs", "esm"],
6
+ clean: true,
7
+ legacyOutput: true,
8
+ dts: options.dts,
9
+ external: ["react"],
10
+ sourcemap: true,
11
+ metafile: options.metafile,
12
+ }));