@proteinjs/ui 4.1.0 → 4.1.1
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/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/container/PageContainer.d.ts +17 -3
- package/dist/src/container/PageContainer.d.ts.map +1 -1
- package/dist/src/container/PageContainer.js +9 -4
- package/dist/src/container/PageContainer.js.map +1 -1
- package/dist/src/formatters.d.ts +37 -0
- package/dist/src/formatters.d.ts.map +1 -0
- package/dist/src/formatters.js +54 -0
- package/dist/src/formatters.js.map +1 -0
- package/dist/src/router/Page.d.ts +0 -2
- package/dist/src/router/Page.d.ts.map +1 -1
- package/dist/src/router/Router.d.ts.map +1 -1
- package/dist/src/router/Router.js +3 -4
- package/dist/src/router/Router.js.map +1 -1
- package/dist/src/table/InfiniteScroll.d.ts.map +1 -1
- package/dist/src/table/InfiniteScroll.js +4 -7
- package/dist/src/table/InfiniteScroll.js.map +1 -1
- package/dist/src/table/Table.d.ts +10 -4
- package/dist/src/table/Table.d.ts.map +1 -1
- package/dist/src/table/Table.js +31 -17
- package/dist/src/table/Table.js.map +1 -1
- package/dist/src/table/tableData.d.ts +7 -3
- package/dist/src/table/tableData.d.ts.map +1 -1
- package/dist/src/table/tableData.js +10 -7
- package/dist/src/table/tableData.js.map +1 -1
- package/generated/index.ts +1 -1
- package/index.ts +2 -0
- package/package.json +5 -6
- package/src/container/PageContainer.tsx +34 -5
- package/src/formatters.ts +69 -0
- package/src/router/Page.ts +0 -1
- package/src/router/Router.tsx +4 -5
- package/src/table/InfiniteScroll.tsx +4 -6
- package/src/table/Table.tsx +66 -22
- package/src/table/tableData.ts +15 -7
- package/LICENSE +0 -21
package/src/table/Table.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useNavigate } from 'react-router-dom';
|
|
3
3
|
import {
|
|
4
4
|
TableContainer,
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
TableContainerOwnProps,
|
|
15
15
|
ToolbarProps,
|
|
16
16
|
CircularProgress,
|
|
17
|
+
TablePaginationProps,
|
|
18
|
+
TableCellProps,
|
|
17
19
|
} from '@mui/material';
|
|
18
20
|
import moment from 'moment';
|
|
19
21
|
import { StringUtil } from '@proteinjs/util';
|
|
@@ -27,13 +29,16 @@ type ColumnValue<T, K extends keyof T> = T[K];
|
|
|
27
29
|
export type CustomRenderer<T, K extends keyof T> = (value: ColumnValue<T, K>, row: T) => React.ReactNode;
|
|
28
30
|
export type ColumnConfig<T> = {
|
|
29
31
|
[K in keyof T]?: {
|
|
32
|
+
cellProps?: TableCellProps;
|
|
30
33
|
renderer?: CustomRenderer<T, K>;
|
|
31
34
|
/** If no header is provided, a default header will be used. Pass in `null` if you'd like the header to be omitted. */
|
|
32
35
|
header?: string | React.ReactNode;
|
|
33
36
|
};
|
|
34
37
|
};
|
|
35
38
|
|
|
36
|
-
type RowClickAction<T> =
|
|
39
|
+
type RowClickAction<T> =
|
|
40
|
+
| string
|
|
41
|
+
| ((row: T, event?: React.MouseEvent) => void | Promise<void> | string | Promise<string>);
|
|
37
42
|
|
|
38
43
|
export type TableProps<T> = {
|
|
39
44
|
title?: string;
|
|
@@ -42,6 +47,7 @@ export type TableProps<T> = {
|
|
|
42
47
|
columnConfig?: ColumnConfig<T>;
|
|
43
48
|
hideColumnHeaders?: boolean;
|
|
44
49
|
tableLoader: TableLoader<T>;
|
|
50
|
+
refetchOnWindowFocus?: boolean;
|
|
45
51
|
/** Setter which will be used to update the row count when rows are loaded */
|
|
46
52
|
setRowCount?: React.Dispatch<React.SetStateAction<number | undefined>>;
|
|
47
53
|
rowOnClick?: RowClickAction<T>;
|
|
@@ -49,8 +55,10 @@ export type TableProps<T> = {
|
|
|
49
55
|
buttons?: TableButton<T>[];
|
|
50
56
|
/** If true, use pagination for table page navigation, if false uses infinite scroll. Defaults to false. */
|
|
51
57
|
pagination?: boolean;
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
/** Props passed into the TablePagination component. This component is only displayed if `pagination` is true. */
|
|
59
|
+
tablePaginationProps?: Partial<TablePaginationProps>;
|
|
60
|
+
/* Number of rows that are loaded per page. */
|
|
61
|
+
rowsPerPage?: number;
|
|
54
62
|
/* Styling set on the root element of the toolbar. */
|
|
55
63
|
toolbarSx?: ToolbarProps['sx'];
|
|
56
64
|
/* Content that will be displayed in the toolbar section of the table. */
|
|
@@ -59,8 +67,15 @@ export type TableProps<T> = {
|
|
|
59
67
|
tableContainerSx?: TableContainerOwnProps['sx'];
|
|
60
68
|
/* Component displayed when there are no rows to display. */
|
|
61
69
|
emptyTableComponent?: React.ReactNode;
|
|
62
|
-
/* Loading skeleton that's displayed before the table rows are first fetched
|
|
70
|
+
/* Loading skeleton that's displayed before the table rows are first fetched.\
|
|
71
|
+
* You can use these class names to target the containers with styling:
|
|
72
|
+
* - `loading-skeleton-table-body`
|
|
73
|
+
* - `loading-skeleton-row`
|
|
74
|
+
* - `loading-skeleton-cell`
|
|
75
|
+
*/
|
|
63
76
|
skeleton?: React.ReactNode;
|
|
77
|
+
/** Loader to display while items are fetching. Only applicable when pagination prop is false. */
|
|
78
|
+
infiniteScrollLoader?: React.ReactNode;
|
|
64
79
|
};
|
|
65
80
|
|
|
66
81
|
export function Table<T>({
|
|
@@ -70,26 +85,43 @@ export function Table<T>({
|
|
|
70
85
|
columnConfig = {},
|
|
71
86
|
hideColumnHeaders = false,
|
|
72
87
|
tableLoader,
|
|
88
|
+
refetchOnWindowFocus = false,
|
|
73
89
|
rowOnClick,
|
|
74
90
|
setRowCount,
|
|
75
91
|
pagination = false,
|
|
76
|
-
|
|
92
|
+
tablePaginationProps,
|
|
93
|
+
rowsPerPage: rowsPerPageProp = 10,
|
|
77
94
|
buttons,
|
|
78
95
|
tableContainerSx,
|
|
79
96
|
toolbarSx,
|
|
80
97
|
toolbarContent,
|
|
81
98
|
emptyTableComponent,
|
|
82
99
|
skeleton,
|
|
100
|
+
infiniteScrollLoader,
|
|
83
101
|
}: TableProps<T>) {
|
|
84
102
|
const infiniteScroll = !pagination;
|
|
85
|
-
const [rowsPerPage, setRowsPerPage] = useState(
|
|
103
|
+
const [rowsPerPage, setRowsPerPage] = useState(rowsPerPageProp);
|
|
86
104
|
const [page, setPage] = useState(0);
|
|
87
105
|
const [selectedRows, setSelectedRows] = useState<{ [key: number]: T }>({});
|
|
88
106
|
const [selectAll, setSelectAll] = useState(false);
|
|
107
|
+
const infScrollContainerRef = useRef<HTMLDivElement>(null);
|
|
89
108
|
const navigate = useNavigate();
|
|
90
109
|
|
|
91
110
|
const { rows, totalRows, isLoading, error, fetchNextPage, hasNextPage, isFetchingNextPage, resetQuery } =
|
|
92
|
-
useTableData<T>(tableLoader, rowsPerPage, page, infiniteScroll, setRowCount);
|
|
111
|
+
useTableData<T>(tableLoader, rowsPerPage, page, infiniteScroll, setRowCount, refetchOnWindowFocus);
|
|
112
|
+
|
|
113
|
+
const maxPage = Math.max(0, Math.ceil((totalRows || 0) / rowsPerPage) - 1);
|
|
114
|
+
|
|
115
|
+
// Adjust page if it exceeds maxPage
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!pagination || isLoading) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (page > maxPage && maxPage >= 0) {
|
|
122
|
+
setPage(maxPage);
|
|
123
|
+
}
|
|
124
|
+
}, [page, maxPage, pagination, isLoading, totalRows]);
|
|
93
125
|
|
|
94
126
|
useEffect(() => {
|
|
95
127
|
resetQuery();
|
|
@@ -115,7 +147,12 @@ export function Table<T>({
|
|
|
115
147
|
}
|
|
116
148
|
}, [fetchNextPage, isFetchingNextPage]);
|
|
117
149
|
|
|
118
|
-
async function handleRowOnClick<T>(
|
|
150
|
+
async function handleRowOnClick<T>(
|
|
151
|
+
row: T,
|
|
152
|
+
event: React.MouseEvent,
|
|
153
|
+
action: RowClickAction<T>,
|
|
154
|
+
navigate: (url: string) => void
|
|
155
|
+
) {
|
|
119
156
|
if (!action) {
|
|
120
157
|
return;
|
|
121
158
|
}
|
|
@@ -131,7 +168,7 @@ export function Table<T>({
|
|
|
131
168
|
}
|
|
132
169
|
|
|
133
170
|
// If action is a function, execute it
|
|
134
|
-
const result = action(row);
|
|
171
|
+
const result = action(row, event);
|
|
135
172
|
|
|
136
173
|
if (result instanceof Promise) {
|
|
137
174
|
// If the result is a Promise, wait for it to resolve
|
|
@@ -250,9 +287,9 @@ export function Table<T>({
|
|
|
250
287
|
</TableRow>
|
|
251
288
|
</TableHead>
|
|
252
289
|
{isLoading && (
|
|
253
|
-
<TableBody>
|
|
254
|
-
<TableRow>
|
|
255
|
-
<TableCell colSpan={totalColumns} align='center' sx={{ py: 3 }}>
|
|
290
|
+
<TableBody className='loading-skeleton-table-body'>
|
|
291
|
+
<TableRow className='loading-skeleton-row'>
|
|
292
|
+
<TableCell colSpan={totalColumns} align='center' className='loading-skeleton-cell' sx={{ py: 3 }}>
|
|
256
293
|
{skeleton ? skeleton : <CircularProgress />}
|
|
257
294
|
</TableCell>
|
|
258
295
|
</TableRow>
|
|
@@ -278,7 +315,11 @@ export function Table<T>({
|
|
|
278
315
|
tabIndex={-1}
|
|
279
316
|
key={index}
|
|
280
317
|
selected={isSelected}
|
|
281
|
-
onClick={
|
|
318
|
+
onClick={
|
|
319
|
+
rowOnClick
|
|
320
|
+
? (event: React.MouseEvent) => handleRowOnClick(row, event, rowOnClick, navigate)
|
|
321
|
+
: undefined
|
|
322
|
+
}
|
|
282
323
|
>
|
|
283
324
|
{buttons && buttons.length > 0 && (
|
|
284
325
|
<TableCell padding='checkbox'>
|
|
@@ -298,7 +339,7 @@ export function Table<T>({
|
|
|
298
339
|
{columns.map((column, index) => {
|
|
299
340
|
const { value: cellValue, isCustomRendered } = formatCellValue(row[column], column, row);
|
|
300
341
|
return (
|
|
301
|
-
<TableCell key={index}>
|
|
342
|
+
<TableCell key={index} {...columnConfig?.[column]?.cellProps}>
|
|
302
343
|
{isCustomRendered ? cellValue : <Typography>{cellValue}</Typography>}
|
|
303
344
|
</TableCell>
|
|
304
345
|
);
|
|
@@ -325,33 +366,36 @@ export function Table<T>({
|
|
|
325
366
|
sx={toolbarSx}
|
|
326
367
|
/>
|
|
327
368
|
)}
|
|
328
|
-
<Box
|
|
369
|
+
<Box ref={infScrollContainerRef} sx={{ width: '100%', flexGrow: 1, overflow: 'auto' }}>
|
|
329
370
|
{infiniteScroll ? (
|
|
330
371
|
<InfiniteScroll
|
|
331
372
|
dataLength={rows.length}
|
|
332
373
|
next={handleFetchNextPage}
|
|
333
374
|
hasMore={!!hasNextPage}
|
|
334
375
|
loader={
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
376
|
+
infiniteScrollLoader || (
|
|
377
|
+
<Typography variant='body2' sx={{ p: 2 }}>
|
|
378
|
+
Loading...
|
|
379
|
+
</Typography>
|
|
380
|
+
)
|
|
338
381
|
}
|
|
339
|
-
scrollableTarget='
|
|
382
|
+
scrollableTarget={React.createElement('div', { ref: infScrollContainerRef })}
|
|
340
383
|
>
|
|
341
384
|
{renderTableContainer()}
|
|
342
385
|
</InfiniteScroll>
|
|
343
386
|
) : (
|
|
344
387
|
renderTableContainer()
|
|
345
388
|
)}
|
|
346
|
-
{pagination &&
|
|
389
|
+
{pagination && (
|
|
347
390
|
<TablePagination
|
|
348
391
|
rowsPerPageOptions={[5, 10, 25, 50, 100, 200]}
|
|
349
392
|
component='div'
|
|
350
393
|
count={totalRows || 0}
|
|
351
394
|
rowsPerPage={rowsPerPage}
|
|
352
|
-
page={page}
|
|
395
|
+
page={isLoading ? page : Math.min(page, maxPage)}
|
|
353
396
|
onPageChange={(event, newPage) => setPage(newPage)}
|
|
354
397
|
onRowsPerPageChange={(event) => updateRowsPerPage(parseInt(event.target.value))}
|
|
398
|
+
{...tablePaginationProps}
|
|
355
399
|
/>
|
|
356
400
|
)}
|
|
357
401
|
</Box>
|
package/src/table/tableData.ts
CHANGED
|
@@ -11,12 +11,18 @@ import {
|
|
|
11
11
|
import { TableLoader, RowWindow } from './TableLoader';
|
|
12
12
|
import { useCallback, useMemo } from 'react';
|
|
13
13
|
|
|
14
|
+
export type InfiniteQueryData<T> = {
|
|
15
|
+
pages: RowWindow<T>[];
|
|
16
|
+
pageParams: any[];
|
|
17
|
+
};
|
|
18
|
+
|
|
14
19
|
export function useTableData<T>(
|
|
15
20
|
tableLoader: TableLoader<T>,
|
|
16
21
|
rowsPerPage: number,
|
|
17
22
|
page: number,
|
|
18
23
|
infiniteScroll: boolean,
|
|
19
|
-
setRowCount?: React.Dispatch<React.SetStateAction<number | undefined
|
|
24
|
+
setRowCount?: React.Dispatch<React.SetStateAction<number | undefined>>,
|
|
25
|
+
refetchOnWindowFocus = false
|
|
20
26
|
) {
|
|
21
27
|
const startIndex = useMemo(() => page * rowsPerPage, [page, rowsPerPage]);
|
|
22
28
|
const endIndex = useMemo(() => startIndex + rowsPerPage, [startIndex, rowsPerPage]);
|
|
@@ -26,7 +32,7 @@ export function useTableData<T>(
|
|
|
26
32
|
isLoading: isPaginatedLoading,
|
|
27
33
|
error: paginatedError,
|
|
28
34
|
refetch: refetchPaginatedData,
|
|
29
|
-
} = usePaginationTableQuery<T>(tableLoader, startIndex, endIndex, !infiniteScroll);
|
|
35
|
+
} = usePaginationTableQuery<T>(tableLoader, startIndex, endIndex, !infiniteScroll, refetchOnWindowFocus);
|
|
30
36
|
|
|
31
37
|
const {
|
|
32
38
|
data: infiniteData,
|
|
@@ -36,7 +42,7 @@ export function useTableData<T>(
|
|
|
36
42
|
isFetchingNextPage,
|
|
37
43
|
error: infiniteError,
|
|
38
44
|
refetch: refetchInfiniteData,
|
|
39
|
-
} = useInfiniteScrollTableQuery<T>(tableLoader, rowsPerPage, setRowCount, infiniteScroll);
|
|
45
|
+
} = useInfiniteScrollTableQuery<T>(tableLoader, rowsPerPage, setRowCount, infiniteScroll, refetchOnWindowFocus);
|
|
40
46
|
|
|
41
47
|
const rows = useMemo(
|
|
42
48
|
() =>
|
|
@@ -90,7 +96,8 @@ export const usePaginationTableQuery = <T>(
|
|
|
90
96
|
tableLoader: TableLoader<T>,
|
|
91
97
|
startIndex: number,
|
|
92
98
|
endIndex: number,
|
|
93
|
-
enabled = false
|
|
99
|
+
enabled = false,
|
|
100
|
+
refetchOnWindowFocus = false
|
|
94
101
|
): UseQueryResult<RowWindow<T>, Error> => {
|
|
95
102
|
const { reactQueryKeys } = tableLoader;
|
|
96
103
|
|
|
@@ -105,7 +112,7 @@ export const usePaginationTableQuery = <T>(
|
|
|
105
112
|
async () => await tableLoader.load(startIndex, endIndex),
|
|
106
113
|
{
|
|
107
114
|
enabled,
|
|
108
|
-
refetchOnWindowFocus
|
|
115
|
+
refetchOnWindowFocus,
|
|
109
116
|
}
|
|
110
117
|
);
|
|
111
118
|
};
|
|
@@ -114,7 +121,8 @@ export const useInfiniteScrollTableQuery = <T>(
|
|
|
114
121
|
tableLoader: TableLoader<T>,
|
|
115
122
|
rowsPerPage: number,
|
|
116
123
|
setRowCount?: React.Dispatch<React.SetStateAction<number | undefined>>,
|
|
117
|
-
enabled = true
|
|
124
|
+
enabled = true,
|
|
125
|
+
refetchOnWindowFocus = false
|
|
118
126
|
): UseInfiniteQueryResult<RowWindow<T>, Error> => {
|
|
119
127
|
const { reactQueryKeys } = tableLoader;
|
|
120
128
|
|
|
@@ -145,7 +153,7 @@ export const useInfiniteScrollTableQuery = <T>(
|
|
|
145
153
|
endIndex: nextStartIndex + rowsPerPage,
|
|
146
154
|
};
|
|
147
155
|
},
|
|
148
|
-
refetchOnWindowFocus
|
|
156
|
+
refetchOnWindowFocus,
|
|
149
157
|
});
|
|
150
158
|
};
|
|
151
159
|
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 Brent Bahry
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|