@dust-tt/sparkle 0.2.225 → 0.2.227-rc
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/dist/cjs/index.js +283 -113
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/DataTable.d.ts +5 -2
- package/dist/esm/components/DataTable.d.ts.map +1 -1
- package/dist/esm/components/DataTable.js +48 -29
- package/dist/esm/components/DataTable.js.map +1 -1
- package/dist/esm/components/Item.d.ts +6 -52
- package/dist/esm/components/Item.d.ts.map +1 -1
- package/dist/esm/components/Item.js.map +1 -1
- package/dist/esm/components/PaginatedCitationsGrid.d.ts.map +1 -1
- package/dist/esm/components/PaginatedCitationsGrid.js +8 -4
- package/dist/esm/components/PaginatedCitationsGrid.js.map +1 -1
- package/dist/esm/components/Pagination.d.ts +5 -4
- package/dist/esm/components/Pagination.d.ts.map +1 -1
- package/dist/esm/components/Pagination.js +35 -35
- package/dist/esm/components/Pagination.js.map +1 -1
- package/dist/esm/hooks/index.d.ts +3 -0
- package/dist/esm/hooks/index.d.ts.map +1 -0
- package/dist/esm/hooks/index.js +3 -0
- package/dist/esm/hooks/index.js.map +1 -0
- package/dist/esm/hooks/useHashParams.d.ts +18 -0
- package/dist/esm/hooks/useHashParams.d.ts.map +1 -0
- package/dist/esm/hooks/useHashParams.js +67 -0
- package/dist/esm/hooks/useHashParams.js.map +1 -0
- package/dist/esm/hooks/usePaginationFromUrl.d.ts +6 -0
- package/dist/esm/hooks/usePaginationFromUrl.d.ts.map +1 -0
- package/dist/esm/hooks/usePaginationFromUrl.js +23 -0
- package/dist/esm/hooks/usePaginationFromUrl.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/stories/DataTable.stories.d.ts +2 -0
- package/dist/esm/stories/DataTable.stories.d.ts.map +1 -1
- package/dist/esm/stories/DataTable.stories.js +133 -106
- package/dist/esm/stories/DataTable.stories.js.map +1 -1
- package/dist/esm/stories/Pagination.stories.d.ts +1 -0
- package/dist/esm/stories/Pagination.stories.d.ts.map +1 -1
- package/dist/esm/stories/Pagination.stories.js +25 -3
- package/dist/esm/stories/Pagination.stories.js.map +1 -1
- package/dist/sparkle.css +4 -0
- package/package.json +1 -1
- package/src/components/DataTable.tsx +114 -75
- package/src/components/Item.tsx +47 -52
- package/src/components/PaginatedCitationsGrid.tsx +11 -6
- package/src/components/Pagination.tsx +48 -45
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useHashParams.ts +95 -0
- package/src/hooks/usePaginationFromUrl.ts +36 -0
- package/src/index.ts +1 -0
- package/src/stories/DataTable.stories.tsx +200 -130
- package/src/stories/Pagination.stories.tsx +38 -10
|
@@ -4,9 +4,12 @@ import {
|
|
|
4
4
|
flexRender,
|
|
5
5
|
getCoreRowModel,
|
|
6
6
|
getFilteredRowModel,
|
|
7
|
+
getPaginationRowModel,
|
|
7
8
|
getSortedRowModel,
|
|
9
|
+
PaginationState,
|
|
8
10
|
SortingFn,
|
|
9
11
|
type SortingState,
|
|
12
|
+
Updater,
|
|
10
13
|
useReactTable,
|
|
11
14
|
} from "@tanstack/react-table";
|
|
12
15
|
import React, { ReactNode, useEffect, useState } from "react";
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
DropdownMenu,
|
|
18
21
|
} from "@sparkle/components/DropdownMenu";
|
|
19
22
|
import { IconButton } from "@sparkle/components/IconButton";
|
|
23
|
+
import { Pagination } from "@sparkle/components/Pagination";
|
|
20
24
|
import { Tooltip } from "@sparkle/components/Tooltip";
|
|
21
25
|
import { ArrowDownIcon, ArrowUpIcon, MoreIcon } from "@sparkle/icons";
|
|
22
26
|
import { classNames } from "@sparkle/lib/utils";
|
|
@@ -35,10 +39,13 @@ interface ColumnBreakpoint {
|
|
|
35
39
|
|
|
36
40
|
interface DataTableProps<TData extends TBaseData> {
|
|
37
41
|
data: TData[];
|
|
42
|
+
totalRowCount?: number;
|
|
38
43
|
columns: ColumnDef<TData, any>[]; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
39
44
|
className?: string;
|
|
40
45
|
filter?: string;
|
|
41
46
|
filterColumn?: string;
|
|
47
|
+
pagination?: PaginationState;
|
|
48
|
+
setPagination?: (pagination: PaginationState) => void;
|
|
42
49
|
initialColumnOrder?: SortingState;
|
|
43
50
|
columnsBreakpoints?: ColumnBreakpoint;
|
|
44
51
|
sortingFn?: SortingFn<TData>;
|
|
@@ -56,12 +63,15 @@ function shouldRenderColumn(
|
|
|
56
63
|
|
|
57
64
|
export function DataTable<TData extends TBaseData>({
|
|
58
65
|
data,
|
|
66
|
+
totalRowCount,
|
|
59
67
|
columns,
|
|
60
68
|
className,
|
|
61
69
|
filter,
|
|
62
70
|
filterColumn,
|
|
63
71
|
initialColumnOrder,
|
|
64
72
|
columnsBreakpoints = {},
|
|
73
|
+
pagination,
|
|
74
|
+
setPagination,
|
|
65
75
|
}: DataTableProps<TData>) {
|
|
66
76
|
const windowSize = useWindowSize();
|
|
67
77
|
const [sorting, setSorting] = useState<SortingState>(
|
|
@@ -69,18 +79,36 @@ export function DataTable<TData extends TBaseData>({
|
|
|
69
79
|
);
|
|
70
80
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
71
81
|
|
|
82
|
+
const isServerSidePagination = !!totalRowCount && totalRowCount > data.length;
|
|
83
|
+
const onPaginationChange =
|
|
84
|
+
pagination && setPagination
|
|
85
|
+
? (updater: Updater<PaginationState>) => {
|
|
86
|
+
const newValue =
|
|
87
|
+
typeof updater === "function" ? updater(pagination) : updater;
|
|
88
|
+
setPagination(newValue);
|
|
89
|
+
}
|
|
90
|
+
: undefined;
|
|
91
|
+
|
|
72
92
|
const table = useReactTable({
|
|
73
93
|
data,
|
|
74
94
|
columns,
|
|
95
|
+
rowCount: totalRowCount,
|
|
96
|
+
manualPagination: isServerSidePagination,
|
|
75
97
|
onSortingChange: setSorting,
|
|
76
98
|
getCoreRowModel: getCoreRowModel(),
|
|
77
99
|
getSortedRowModel: getSortedRowModel(),
|
|
78
100
|
getFilteredRowModel: getFilteredRowModel(),
|
|
101
|
+
getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
|
|
79
102
|
onColumnFiltersChange: setColumnFilters,
|
|
80
103
|
state: {
|
|
81
104
|
columnFilters,
|
|
82
|
-
sorting,
|
|
105
|
+
sorting: isServerSidePagination ? undefined : sorting,
|
|
106
|
+
pagination,
|
|
107
|
+
},
|
|
108
|
+
initialState: {
|
|
109
|
+
pagination,
|
|
83
110
|
},
|
|
111
|
+
onPaginationChange: onPaginationChange,
|
|
84
112
|
});
|
|
85
113
|
|
|
86
114
|
useEffect(() => {
|
|
@@ -90,81 +118,92 @@ export function DataTable<TData extends TBaseData>({
|
|
|
90
118
|
}, [filter, filterColumn]);
|
|
91
119
|
|
|
92
120
|
return (
|
|
93
|
-
<
|
|
94
|
-
<DataTable.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{headerGroup.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
>
|
|
113
|
-
<div className="s-flex s-items-center s-space-x-1 s-whitespace-nowrap">
|
|
114
|
-
{flexRender(
|
|
115
|
-
header.column.columnDef.header,
|
|
116
|
-
header.getContext()
|
|
117
|
-
)}
|
|
118
|
-
{header.column.getCanSort() && (
|
|
119
|
-
<Icon
|
|
120
|
-
visual={
|
|
121
|
-
header.column.getIsSorted() === "asc"
|
|
122
|
-
? ArrowUpIcon
|
|
123
|
-
: header.column.getIsSorted() === "desc"
|
|
124
|
-
? ArrowDownIcon
|
|
125
|
-
: ArrowDownIcon
|
|
126
|
-
}
|
|
127
|
-
size="xs"
|
|
128
|
-
className={classNames(
|
|
129
|
-
"s-ml-1",
|
|
130
|
-
header.column.getIsSorted()
|
|
131
|
-
? "s-opacity-100"
|
|
132
|
-
: "s-opacity-0"
|
|
133
|
-
)}
|
|
134
|
-
/>
|
|
121
|
+
<div className="s-flex s-flex-col s-gap-2">
|
|
122
|
+
<DataTable.Root className={className}>
|
|
123
|
+
<DataTable.Header>
|
|
124
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
125
|
+
<DataTable.Row key={headerGroup.id}>
|
|
126
|
+
{headerGroup.headers.map((header) => {
|
|
127
|
+
const breakpoint = columnsBreakpoints[header.id];
|
|
128
|
+
if (
|
|
129
|
+
!windowSize.width ||
|
|
130
|
+
!shouldRenderColumn(windowSize.width, breakpoint)
|
|
131
|
+
) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return (
|
|
135
|
+
<DataTable.Head
|
|
136
|
+
key={header.id}
|
|
137
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
138
|
+
className={classNames(
|
|
139
|
+
header.column.getCanSort() ? "s-cursor-pointer" : ""
|
|
135
140
|
)}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
141
|
+
>
|
|
142
|
+
<div className="s-flex s-items-center s-space-x-1 s-whitespace-nowrap">
|
|
143
|
+
{flexRender(
|
|
144
|
+
header.column.columnDef.header,
|
|
145
|
+
header.getContext()
|
|
146
|
+
)}
|
|
147
|
+
{header.column.getCanSort() && (
|
|
148
|
+
<Icon
|
|
149
|
+
visual={
|
|
150
|
+
header.column.getIsSorted() === "asc"
|
|
151
|
+
? ArrowUpIcon
|
|
152
|
+
: header.column.getIsSorted() === "desc"
|
|
153
|
+
? ArrowDownIcon
|
|
154
|
+
: ArrowDownIcon
|
|
155
|
+
}
|
|
156
|
+
size="xs"
|
|
157
|
+
className={classNames(
|
|
158
|
+
"s-ml-1",
|
|
159
|
+
header.column.getIsSorted()
|
|
160
|
+
? "s-opacity-100"
|
|
161
|
+
: "s-opacity-0"
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
</DataTable.Head>
|
|
167
|
+
);
|
|
168
|
+
})}
|
|
169
|
+
</DataTable.Row>
|
|
170
|
+
))}
|
|
171
|
+
</DataTable.Header>
|
|
172
|
+
<DataTable.Body>
|
|
173
|
+
{table.getRowModel().rows.map((row) => (
|
|
174
|
+
<DataTable.Row
|
|
175
|
+
key={row.id}
|
|
176
|
+
onClick={row.original.onClick}
|
|
177
|
+
moreMenuItems={row.original.moreMenuItems}
|
|
178
|
+
>
|
|
179
|
+
{row.getVisibleCells().map((cell) => {
|
|
180
|
+
const breakpoint = columnsBreakpoints[cell.column.id];
|
|
181
|
+
if (
|
|
182
|
+
!windowSize.width ||
|
|
183
|
+
!shouldRenderColumn(windowSize.width, breakpoint)
|
|
184
|
+
) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return (
|
|
188
|
+
<DataTable.Cell key={cell.id}>
|
|
189
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
190
|
+
</DataTable.Cell>
|
|
191
|
+
);
|
|
192
|
+
})}
|
|
193
|
+
</DataTable.Row>
|
|
194
|
+
))}
|
|
195
|
+
</DataTable.Body>
|
|
196
|
+
</DataTable.Root>
|
|
197
|
+
{pagination && (
|
|
198
|
+
<div className="s-p-1">
|
|
199
|
+
<Pagination
|
|
200
|
+
pagination={table.getState().pagination}
|
|
201
|
+
setPagination={table.setPagination}
|
|
202
|
+
rowCount={table.getRowCount()}
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
168
207
|
);
|
|
169
208
|
}
|
|
170
209
|
|
package/src/components/Item.tsx
CHANGED
|
@@ -184,30 +184,26 @@ export function Item({
|
|
|
184
184
|
);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
disabled
|
|
190
|
-
|
|
191
|
-
label: string;
|
|
192
|
-
icon?: ComponentType;
|
|
193
|
-
className?: string;
|
|
194
|
-
href?: string;
|
|
195
|
-
}
|
|
187
|
+
type EntryItemProps = Pick<
|
|
188
|
+
ItemProps,
|
|
189
|
+
"onClick" | "disabled" | "selected" | "label" | "icon" | "className" | "link"
|
|
190
|
+
>;
|
|
196
191
|
|
|
197
192
|
Item.Entry = function (props: EntryItemProps) {
|
|
198
193
|
return <Item {...props} spacing="sm" style="item" hasAction={"hover"} />;
|
|
199
194
|
};
|
|
200
195
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
196
|
+
type AvatarItemProps = Pick<
|
|
197
|
+
ItemProps,
|
|
198
|
+
| "className"
|
|
199
|
+
| "description"
|
|
200
|
+
| "disabled"
|
|
201
|
+
| "hasAction"
|
|
202
|
+
| "label"
|
|
203
|
+
| "link"
|
|
204
|
+
| "onClick"
|
|
205
|
+
| "visual"
|
|
206
|
+
>;
|
|
211
207
|
|
|
212
208
|
Item.Avatar = function ({
|
|
213
209
|
description,
|
|
@@ -225,34 +221,28 @@ Item.Avatar = function ({
|
|
|
225
221
|
);
|
|
226
222
|
};
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
224
|
+
type NavigationListItemProps = Pick<
|
|
225
|
+
ItemProps,
|
|
226
|
+
| "action"
|
|
227
|
+
| "className"
|
|
228
|
+
| "description"
|
|
229
|
+
| "disabled"
|
|
230
|
+
| "hasAction"
|
|
231
|
+
| "icon"
|
|
232
|
+
| "label"
|
|
233
|
+
| "link"
|
|
234
|
+
| "onClick"
|
|
235
|
+
| "selected"
|
|
236
|
+
>;
|
|
241
237
|
|
|
242
238
|
Item.Navigation = function (props: NavigationListItemProps) {
|
|
243
239
|
return <Item {...props} style="action" spacing="md" />;
|
|
244
240
|
};
|
|
245
241
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
label
|
|
249
|
-
|
|
250
|
-
visual?: string | React.ReactNode;
|
|
251
|
-
icon?: ComponentType;
|
|
252
|
-
className?: string;
|
|
253
|
-
href?: string;
|
|
254
|
-
target?: string;
|
|
255
|
-
}
|
|
242
|
+
type LinkItemProps = Pick<
|
|
243
|
+
ItemProps,
|
|
244
|
+
"onClick" | "label" | "description" | "visual" | "icon" | "className" | "link"
|
|
245
|
+
>;
|
|
256
246
|
|
|
257
247
|
Item.Link = function ({ ...props }: LinkItemProps) {
|
|
258
248
|
return (
|
|
@@ -267,19 +257,24 @@ Item.Link = function ({ ...props }: LinkItemProps) {
|
|
|
267
257
|
);
|
|
268
258
|
};
|
|
269
259
|
|
|
270
|
-
interface
|
|
260
|
+
interface DropdownListItemBaseProps {
|
|
271
261
|
style?: "default" | "warning";
|
|
272
|
-
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
273
|
-
selected?: boolean;
|
|
274
|
-
disabled?: boolean;
|
|
275
|
-
label: string;
|
|
276
|
-
description?: string;
|
|
277
|
-
visual?: string | React.ReactNode;
|
|
278
|
-
icon?: ComponentType;
|
|
279
|
-
className?: string;
|
|
280
|
-
link?: LinkProps;
|
|
281
262
|
}
|
|
282
263
|
|
|
264
|
+
type DropdownListItemProps = DropdownListItemBaseProps &
|
|
265
|
+
Pick<
|
|
266
|
+
ItemProps,
|
|
267
|
+
| "className"
|
|
268
|
+
| "description"
|
|
269
|
+
| "disabled"
|
|
270
|
+
| "icon"
|
|
271
|
+
| "label"
|
|
272
|
+
| "link"
|
|
273
|
+
| "onClick"
|
|
274
|
+
| "selected"
|
|
275
|
+
| "visual"
|
|
276
|
+
>;
|
|
277
|
+
|
|
283
278
|
Item.Dropdown = function ({ style, ...props }: DropdownListItemProps) {
|
|
284
279
|
return (
|
|
285
280
|
<Item
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PaginationState } from "@tanstack/react-table";
|
|
1
2
|
import { useState } from "react";
|
|
2
3
|
import React from "react";
|
|
3
4
|
|
|
@@ -21,7 +22,10 @@ export function PaginatedCitationsGrid({
|
|
|
21
22
|
items,
|
|
22
23
|
maxItemsPerPage = 9,
|
|
23
24
|
}: PaginatedCitationsGridProps) {
|
|
24
|
-
const [
|
|
25
|
+
const [pagination, setPagination] = useState<PaginationState>({
|
|
26
|
+
pageIndex: 0,
|
|
27
|
+
pageSize: maxItemsPerPage,
|
|
28
|
+
});
|
|
25
29
|
const cols = 3;
|
|
26
30
|
const rows = Math.ceil(Math.min(maxItemsPerPage, items.length) / cols);
|
|
27
31
|
|
|
@@ -29,10 +33,11 @@ export function PaginatedCitationsGrid({
|
|
|
29
33
|
return null;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
const { pageIndex, pageSize } = pagination;
|
|
32
37
|
// Calculate start index.
|
|
33
|
-
const startIndex =
|
|
38
|
+
const startIndex = pageIndex * pageSize;
|
|
34
39
|
// Slice items for current page.
|
|
35
|
-
const paginatedItems = items.slice(startIndex, startIndex +
|
|
40
|
+
const paginatedItems = items.slice(startIndex, startIndex + pageSize);
|
|
36
41
|
|
|
37
42
|
return (
|
|
38
43
|
<div className="s-flex s-w-full s-flex-col">
|
|
@@ -65,12 +70,12 @@ export function PaginatedCitationsGrid({
|
|
|
65
70
|
)}
|
|
66
71
|
>
|
|
67
72
|
<Pagination
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
rowCount={items.length}
|
|
74
|
+
pagination={pagination}
|
|
75
|
+
setPagination={setPagination}
|
|
70
76
|
size="xs"
|
|
71
77
|
showDetails={false}
|
|
72
78
|
showPageButtons={false}
|
|
73
|
-
onButtonClick={(pageNb) => setCurrentPage(pageNb)}
|
|
74
79
|
/>
|
|
75
80
|
</div>
|
|
76
81
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { PaginationState } from "@tanstack/react-table";
|
|
1
2
|
import React, { useCallback } from "react";
|
|
2
|
-
import { useState } from "react";
|
|
3
3
|
|
|
4
4
|
import { ChevronLeftIcon, ChevronRightIcon } from "@sparkle/icons/solid";
|
|
5
5
|
import { classNames } from "@sparkle/lib/utils";
|
|
@@ -11,42 +11,49 @@ type Size = "sm" | "xs";
|
|
|
11
11
|
const pagesShownInControls = 7;
|
|
12
12
|
|
|
13
13
|
interface PaginationProps {
|
|
14
|
-
itemsCount: number;
|
|
15
|
-
maxItemsPerPage: number;
|
|
16
|
-
onButtonClick: (pageNumber: number) => void;
|
|
17
14
|
size?: Size;
|
|
18
15
|
showDetails?: boolean;
|
|
19
16
|
showPageButtons?: boolean;
|
|
17
|
+
rowCount: number;
|
|
18
|
+
pagination: PaginationState;
|
|
19
|
+
setPagination: (pagination: PaginationState) => void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function Pagination({
|
|
23
|
-
itemsCount,
|
|
24
|
-
maxItemsPerPage,
|
|
25
|
-
onButtonClick,
|
|
26
23
|
size = "sm",
|
|
27
24
|
showDetails = true,
|
|
28
25
|
showPageButtons = true,
|
|
26
|
+
rowCount,
|
|
27
|
+
pagination,
|
|
28
|
+
setPagination,
|
|
29
29
|
}: PaginationProps) {
|
|
30
|
-
|
|
30
|
+
// pageIndex is 0-based
|
|
31
|
+
const { pageIndex, pageSize } = pagination;
|
|
32
|
+
|
|
33
|
+
const numPages = Math.ceil(rowCount / pageSize);
|
|
34
|
+
|
|
35
|
+
const canNextPage = pagination.pageIndex < numPages - 1;
|
|
36
|
+
const canPreviousPage = pageIndex > 0;
|
|
37
|
+
const nextPage = () => setPagination({ pageSize, pageIndex: pageIndex + 1 });
|
|
38
|
+
const previousPage = () =>
|
|
39
|
+
setPagination({ pageSize, pageIndex: pageIndex - 1 });
|
|
40
|
+
|
|
31
41
|
const controlsAreHidden = Boolean(numPages <= 1);
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
? currentPage * maxItemsPerPage
|
|
38
|
-
: itemsCount;
|
|
42
|
+
const firstItemOnPageIndex = pageIndex * pageSize + 1;
|
|
43
|
+
const lastItemOnPageIndex =
|
|
44
|
+
rowCount > (pageIndex + 1) * pageSize
|
|
45
|
+
? (pageIndex + 1) * pageSize
|
|
46
|
+
: rowCount;
|
|
39
47
|
|
|
40
48
|
const onPaginationButtonClick = useCallback(
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
onButtonClick(pageNb);
|
|
49
|
+
(pageIndex: number) => {
|
|
50
|
+
setPagination({ pageSize, pageIndex });
|
|
44
51
|
},
|
|
45
|
-
[
|
|
52
|
+
[pageIndex, setPagination]
|
|
46
53
|
);
|
|
47
54
|
|
|
48
55
|
const pageButtons: React.ReactNode[] = getPageButtons(
|
|
49
|
-
|
|
56
|
+
pageIndex,
|
|
50
57
|
numPages,
|
|
51
58
|
pagesShownInControls,
|
|
52
59
|
onPaginationButtonClick,
|
|
@@ -73,11 +80,9 @@ export function Pagination({
|
|
|
73
80
|
label="previous"
|
|
74
81
|
labelVisible={false}
|
|
75
82
|
disabledTooltip={true}
|
|
76
|
-
disabled={
|
|
83
|
+
disabled={!canPreviousPage}
|
|
77
84
|
icon={ChevronLeftIcon}
|
|
78
|
-
onClick={
|
|
79
|
-
onPaginationButtonClick(currentPage - 1);
|
|
80
|
-
}}
|
|
85
|
+
onClick={previousPage}
|
|
81
86
|
/>
|
|
82
87
|
|
|
83
88
|
<div
|
|
@@ -96,11 +101,9 @@ export function Pagination({
|
|
|
96
101
|
label="next"
|
|
97
102
|
labelVisible={false}
|
|
98
103
|
disabledTooltip={true}
|
|
99
|
-
disabled={
|
|
104
|
+
disabled={!canNextPage}
|
|
100
105
|
icon={ChevronRightIcon}
|
|
101
|
-
onClick={
|
|
102
|
-
onPaginationButtonClick(currentPage + 1);
|
|
103
|
-
}}
|
|
106
|
+
onClick={nextPage}
|
|
104
107
|
/>
|
|
105
108
|
</div>
|
|
106
109
|
|
|
@@ -112,8 +115,8 @@ export function Pagination({
|
|
|
112
115
|
)}
|
|
113
116
|
>
|
|
114
117
|
{controlsAreHidden
|
|
115
|
-
? `${
|
|
116
|
-
: `Showing ${
|
|
118
|
+
? `${rowCount} items`
|
|
119
|
+
: `Showing ${firstItemOnPageIndex}-${lastItemOnPageIndex} of ${rowCount} items`}
|
|
117
120
|
</span>
|
|
118
121
|
</div>
|
|
119
122
|
);
|
|
@@ -135,7 +138,7 @@ function renderPageNumber(
|
|
|
135
138
|
)}
|
|
136
139
|
onClick={() => onPageClick(pageNumber)}
|
|
137
140
|
>
|
|
138
|
-
{pageNumber}
|
|
141
|
+
{pageNumber + 1}
|
|
139
142
|
</button>
|
|
140
143
|
);
|
|
141
144
|
}
|
|
@@ -164,7 +167,7 @@ function getPageButtons(
|
|
|
164
167
|
|
|
165
168
|
// If total pages are less than or equal to slots, show all pages
|
|
166
169
|
if (totalPages <= slots) {
|
|
167
|
-
for (let i =
|
|
170
|
+
for (let i = 0; i < totalPages; i++) {
|
|
168
171
|
pagination.push(renderPageNumber(i, currentPage, onPageClick, size));
|
|
169
172
|
}
|
|
170
173
|
return pagination;
|
|
@@ -174,25 +177,23 @@ function getPageButtons(
|
|
|
174
177
|
const halfSlots = Math.floor(remainingSlots / 2);
|
|
175
178
|
|
|
176
179
|
// Ensure current page is within bounds
|
|
177
|
-
currentPage = Math.max(
|
|
178
|
-
|
|
179
|
-
pagination.push(renderPageNumber(1, currentPage, onPageClick, size)); // Always show the first page
|
|
180
|
+
currentPage = Math.max(0, Math.min(currentPage, totalPages - 1));
|
|
180
181
|
|
|
182
|
+
pagination.push(renderPageNumber(0, currentPage, onPageClick, size)); // Always show the first page
|
|
181
183
|
// Determine the range of pages to display
|
|
182
184
|
let start, end;
|
|
183
|
-
if (currentPage <= halfSlots +
|
|
184
|
-
start =
|
|
185
|
-
end = remainingSlots;
|
|
186
|
-
} else if (currentPage >= totalPages - halfSlots -
|
|
187
|
-
start = totalPages - remainingSlots
|
|
188
|
-
end = totalPages -
|
|
185
|
+
if (currentPage <= halfSlots + 1) {
|
|
186
|
+
start = 1;
|
|
187
|
+
end = remainingSlots - 1;
|
|
188
|
+
} else if (currentPage >= totalPages - halfSlots - 2) {
|
|
189
|
+
start = totalPages - remainingSlots;
|
|
190
|
+
end = totalPages - 2;
|
|
189
191
|
} else {
|
|
190
192
|
start = currentPage - halfSlots + 1;
|
|
191
193
|
end = currentPage + halfSlots - 1;
|
|
192
194
|
}
|
|
193
|
-
|
|
194
195
|
// Add ellipsis if there is a gap between the first page and the start of the range
|
|
195
|
-
if (start >
|
|
196
|
+
if (start > 1) {
|
|
196
197
|
pagination.push(renderEllipses(size));
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -202,11 +203,13 @@ function getPageButtons(
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
// Add ellipsis if there is a gap between the end of the range and the last page
|
|
205
|
-
if (end < totalPages -
|
|
206
|
+
if (end < totalPages - 2) {
|
|
206
207
|
pagination.push(renderEllipses(size));
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
pagination.push(
|
|
210
|
+
pagination.push(
|
|
211
|
+
renderPageNumber(totalPages - 1, currentPage, onPageClick, size)
|
|
212
|
+
); // Always show the last page
|
|
210
213
|
|
|
211
214
|
return pagination;
|
|
212
215
|
}
|