@applica-software-guru/react-admin 1.5.280 → 1.5.282
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/components/Pagination/Pagination.d.ts +15 -0
- package/dist/components/Pagination/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination/PaginationActions.d.ts +12 -0
- package/dist/components/Pagination/PaginationActions.d.ts.map +1 -0
- package/dist/components/ra-buttons/EditInDialogButton.d.ts +2 -2
- package/dist/components/ra-lists/List.d.ts.map +1 -1
- package/dist/components/ra-lists/SimpleList.d.ts +2 -2
- package/dist/react-admin.cjs.js +54 -54
- package/dist/react-admin.cjs.js.gz +0 -0
- package/dist/react-admin.cjs.js.map +1 -1
- package/dist/react-admin.es.js +8316 -8176
- package/dist/react-admin.es.js.gz +0 -0
- package/dist/react-admin.es.js.map +1 -1
- package/dist/react-admin.umd.js +52 -52
- package/dist/react-admin.umd.js.gz +0 -0
- package/dist/react-admin.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Pagination/Pagination.tsx +184 -0
- package/src/components/Pagination/PaginationActions.tsx +84 -0
- package/src/components/Pagination/index.ts +2 -0
- package/src/components/ra-buttons/EditInDialogButton.tsx +2 -2
- package/src/components/ra-lists/List.tsx +2 -1
- package/src/playground/App.jsx +2 -2
package/package.json
CHANGED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { FC, ReactElement, memo, useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { TablePagination, TablePaginationBaseProps, Theme, Toolbar, useMediaQuery } from '@mui/material';
|
|
4
|
+
import {
|
|
5
|
+
ComponentPropType,
|
|
6
|
+
ListPaginationContextValue,
|
|
7
|
+
sanitizeListRestProps,
|
|
8
|
+
useListPaginationContext,
|
|
9
|
+
useTranslate
|
|
10
|
+
} from 'ra-core';
|
|
11
|
+
|
|
12
|
+
import { PaginationActions, PaginationActionsProps } from './PaginationActions';
|
|
13
|
+
import { debounce } from 'lodash';
|
|
14
|
+
|
|
15
|
+
const Pagination: FC<PaginationProps> = memo((props) => {
|
|
16
|
+
const { rowsPerPageOptions = DefaultRowsPerPageOptions, actions, limit = null, ...rest } = props;
|
|
17
|
+
const { isLoading, hasNextPage, page, perPage, total, setPage, setPerPage } = useListPaginationContext(props);
|
|
18
|
+
const translate = useTranslate();
|
|
19
|
+
const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
|
|
20
|
+
const [currentPage, setCurrentPage] = useState(0); // Stato per la UI
|
|
21
|
+
|
|
22
|
+
const totalPages = useMemo(() => {
|
|
23
|
+
return total != null ? Math.ceil(total / perPage) : undefined;
|
|
24
|
+
}, [perPage, total]);
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
|
+
const debouncedPageChange = useCallback(
|
|
28
|
+
debounce((page) => {
|
|
29
|
+
setPage(page + 1);
|
|
30
|
+
}, 500),
|
|
31
|
+
[setPage]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Warning: Material UI's page is 0-based
|
|
36
|
+
*/
|
|
37
|
+
const handlePageChange = useCallback(
|
|
38
|
+
(event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, page: number) => {
|
|
39
|
+
if (!event) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
if (page < 0 || (totalPages !== undefined && page > totalPages - 1)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
translate('ra.navigation.page_out_of_boundaries', {
|
|
47
|
+
page: page + 1
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const arrowSelected =
|
|
53
|
+
((event.target as HTMLElement).dataset?.testid || (event.target as HTMLElement).classList?.value) ?? '';
|
|
54
|
+
// check if user is clicking on the arrows or on the numbers
|
|
55
|
+
const isArrowClick =
|
|
56
|
+
arrowSelected.includes('MuiPaginationItem-previousNext') ||
|
|
57
|
+
arrowSelected.includes('NavigateBeforeIcon') ||
|
|
58
|
+
arrowSelected.includes('NavigateNextIcon') ||
|
|
59
|
+
arrowSelected.includes('KeyboardArrowLeftIcon') ||
|
|
60
|
+
arrowSelected.includes('KeyboardArrowRightIcon');
|
|
61
|
+
|
|
62
|
+
setCurrentPage(page);
|
|
63
|
+
|
|
64
|
+
if (isArrowClick) {
|
|
65
|
+
// apply debounced API call for arrows clicks
|
|
66
|
+
debouncedPageChange(page);
|
|
67
|
+
} else {
|
|
68
|
+
// apply immediate API call for number clicks
|
|
69
|
+
setPage(page + 1);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[debouncedPageChange, setPage, translate, totalPages]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const handlePerPageChange = useCallback(
|
|
76
|
+
(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
|
77
|
+
setPerPage(Number(event.target.value));
|
|
78
|
+
},
|
|
79
|
+
[setPerPage]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const labelDisplayedRows = useCallback(
|
|
83
|
+
({ from, to, count }: { from: number; to: number; count: number }) =>
|
|
84
|
+
count === -1 && hasNextPage
|
|
85
|
+
? translate('ra.navigation.partial_page_range_info', {
|
|
86
|
+
offsetBegin: from,
|
|
87
|
+
offsetEnd: to,
|
|
88
|
+
_: `%{from}-%{to} of more than %{to}`
|
|
89
|
+
})
|
|
90
|
+
: translate('ra.navigation.page_range_info', {
|
|
91
|
+
offsetBegin: from,
|
|
92
|
+
offsetEnd: to,
|
|
93
|
+
total: count === -1 ? to : count,
|
|
94
|
+
_: `%{from}-%{to} of %{count === -1 ? to : count}`
|
|
95
|
+
}),
|
|
96
|
+
[translate, hasNextPage]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const labelItem = useCallback(
|
|
100
|
+
(type: string) => translate(`ra.navigation.${type}`, { _: `Go to ${type} page` }),
|
|
101
|
+
[translate]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (isLoading) {
|
|
105
|
+
return <Toolbar variant="dense" />;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Avoid rendering TablePagination if "page" value is invalid
|
|
109
|
+
if (total === 0 || page < 1 || (totalPages !== undefined && page > totalPages)) {
|
|
110
|
+
if (limit != null && process.env.NODE_ENV === 'development') {
|
|
111
|
+
console.warn(
|
|
112
|
+
'The Pagination limit prop is deprecated. Empty state should be handled by the component displaying data (Datagrid, SimpleList).'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (isSmall) {
|
|
119
|
+
return (
|
|
120
|
+
<TablePagination
|
|
121
|
+
count={total == null ? -1 : total}
|
|
122
|
+
rowsPerPage={perPage}
|
|
123
|
+
page={currentPage}
|
|
124
|
+
onPageChange={handlePageChange}
|
|
125
|
+
rowsPerPageOptions={emptyArray}
|
|
126
|
+
component="span"
|
|
127
|
+
labelDisplayedRows={labelDisplayedRows}
|
|
128
|
+
{...sanitizeListRestProps(rest)}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const ActionsComponent = actions
|
|
134
|
+
? actions // overridden by caller
|
|
135
|
+
: !isLoading && total != null
|
|
136
|
+
? PaginationActions // regular navigation
|
|
137
|
+
: undefined; // partial navigation (uses default TablePaginationActions)
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<TablePagination
|
|
141
|
+
count={total == null ? -1 : total}
|
|
142
|
+
rowsPerPage={perPage}
|
|
143
|
+
page={currentPage}
|
|
144
|
+
onPageChange={handlePageChange}
|
|
145
|
+
onRowsPerPageChange={handlePerPageChange}
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
ActionsComponent={ActionsComponent}
|
|
148
|
+
nextIconButtonProps={{
|
|
149
|
+
disabled: !hasNextPage
|
|
150
|
+
}}
|
|
151
|
+
component="span"
|
|
152
|
+
labelRowsPerPage={translate('ra.navigation.page_rows_per_page')}
|
|
153
|
+
labelDisplayedRows={labelDisplayedRows}
|
|
154
|
+
getItemAriaLabel={labelItem}
|
|
155
|
+
rowsPerPageOptions={rowsPerPageOptions}
|
|
156
|
+
{...sanitizeListRestProps(rest)}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
Pagination.propTypes = {
|
|
162
|
+
actions: ComponentPropType,
|
|
163
|
+
limit: PropTypes.element,
|
|
164
|
+
rowsPerPageOptions: PropTypes.arrayOf(
|
|
165
|
+
PropTypes.oneOfType([
|
|
166
|
+
PropTypes.number,
|
|
167
|
+
PropTypes.shape({
|
|
168
|
+
label: PropTypes.string.isRequired,
|
|
169
|
+
value: PropTypes.number.isRequired
|
|
170
|
+
})
|
|
171
|
+
])
|
|
172
|
+
) as PropTypes.Validator<(number | { label: string; value: number })[] | null | undefined>
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const DefaultRowsPerPageOptions = [5, 10, 25, 50];
|
|
176
|
+
const emptyArray: any[] = [];
|
|
177
|
+
|
|
178
|
+
interface PaginationProps extends TablePaginationBaseProps, Partial<ListPaginationContextValue> {
|
|
179
|
+
rowsPerPageOptions?: Array<number | { label: string; value: number }>;
|
|
180
|
+
actions?: FC<PaginationActionsProps>;
|
|
181
|
+
limit?: ReactElement;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { Pagination };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FC, memo } from 'react';
|
|
2
|
+
import { styled } from '@mui/material/styles';
|
|
3
|
+
import { Pagination, PaginationProps } from '@mui/material';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { useTranslate } from 'ra-core';
|
|
6
|
+
|
|
7
|
+
const PaginationActions: FC<PaginationActionsProps> = memo((props) => {
|
|
8
|
+
const { page, rowsPerPage, count, onPageChange, size = 'small', className, ...rest } = props;
|
|
9
|
+
const translate = useTranslate();
|
|
10
|
+
|
|
11
|
+
const nbPages = Math.ceil(count / rowsPerPage) || 1;
|
|
12
|
+
|
|
13
|
+
if (nbPages === 1) {
|
|
14
|
+
return <Root className={className} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getItemAriaLabel(type: 'page' | 'first' | 'last' | 'next' | 'previous', page: number, selected: boolean) {
|
|
18
|
+
if (type === 'page') {
|
|
19
|
+
return selected
|
|
20
|
+
? translate('ra.navigation.current_page', {
|
|
21
|
+
page,
|
|
22
|
+
_: `page ${page}`
|
|
23
|
+
})
|
|
24
|
+
: translate('ra.navigation.page', {
|
|
25
|
+
page,
|
|
26
|
+
_: `Go to page ${page}`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return translate(`ra.navigation.${type}`, { _: `Go to ${type} page` });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Root className={className}>
|
|
34
|
+
<Pagination
|
|
35
|
+
size={size}
|
|
36
|
+
count={nbPages}
|
|
37
|
+
// <TablePagination>, the parent, uses 0-based pagination
|
|
38
|
+
// while <Pagination> uses 1-based pagination
|
|
39
|
+
page={page + 1}
|
|
40
|
+
onChange={(e: any, page) => onPageChange(e, page - 1)}
|
|
41
|
+
{...sanitizeRestProps(rest)}
|
|
42
|
+
getItemAriaLabel={getItemAriaLabel}
|
|
43
|
+
/>
|
|
44
|
+
</Root>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
interface PaginationActionsProps extends PaginationProps {
|
|
49
|
+
page: number;
|
|
50
|
+
rowsPerPage: number;
|
|
51
|
+
count: number;
|
|
52
|
+
onPageChange: (event: MouseEvent, page: number) => void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* PaginationActions propTypes are copied over from Material UI’s
|
|
56
|
+
* TablePaginationActions propTypes. See
|
|
57
|
+
* https://github.com/mui/material-ui/blob/869692ecf3812bc4577ed4dde81a9911c5949695/packages/material-ui/src/TablePaginationActions/TablePaginationActions.js#L53-L85
|
|
58
|
+
* for reference.
|
|
59
|
+
*/
|
|
60
|
+
PaginationActions.propTypes = {
|
|
61
|
+
count: PropTypes.number.isRequired,
|
|
62
|
+
onPageChange: PropTypes.func.isRequired,
|
|
63
|
+
page: PropTypes.number.isRequired,
|
|
64
|
+
rowsPerPage: PropTypes.number.isRequired,
|
|
65
|
+
color: PropTypes.oneOf(['primary', 'secondary', 'standard']),
|
|
66
|
+
size: PropTypes.oneOf(['small', 'medium', 'large'])
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const PREFIX = 'RaPaginationActions';
|
|
70
|
+
|
|
71
|
+
const Root = styled('div', {
|
|
72
|
+
name: PREFIX,
|
|
73
|
+
overridesResolver: (styles) => styles.root
|
|
74
|
+
})(() => ({
|
|
75
|
+
flexShrink: 0,
|
|
76
|
+
ml: 4
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
function sanitizeRestProps({ nextIconButtonProps, backIconButtonProps, slotProps, ...rest }: any) {
|
|
80
|
+
return rest;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { PaginationActions };
|
|
84
|
+
export type { PaginationActionsProps };
|
|
@@ -66,11 +66,11 @@ type EditInDialogButtonProps = PropsWithChildren &
|
|
|
66
66
|
* console.log(values); // { title: 'Hello World' }
|
|
67
67
|
* closeDialog();
|
|
68
68
|
* }
|
|
69
|
-
* <
|
|
69
|
+
* <EditInDialogButton onSubmit={handleSubmit}>
|
|
70
70
|
* <SimpleForm>
|
|
71
71
|
* <TextInput source="title" />
|
|
72
72
|
* </SimpleForm>
|
|
73
|
-
* </
|
|
73
|
+
* </EditInDialogButton>
|
|
74
74
|
*
|
|
75
75
|
*/
|
|
76
76
|
onSubmit?: SubmitFunction;
|
|
@@ -4,6 +4,7 @@ import { styled } from '@mui/system';
|
|
|
4
4
|
import { ListBase, RaRecord } from 'ra-core';
|
|
5
5
|
import { ReactElement } from 'react';
|
|
6
6
|
import { ListProps } from 'react-admin';
|
|
7
|
+
import { Pagination } from '@/components/Pagination/Pagination';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* List page component
|
|
@@ -191,7 +192,7 @@ const StyledList = styled(RaList, { slot: 'root' })(({ theme }) => ({
|
|
|
191
192
|
function List(props: ListProps): ReactElement {
|
|
192
193
|
return (
|
|
193
194
|
<MainCard content={false}>
|
|
194
|
-
<StyledList {...props} />
|
|
195
|
+
<StyledList {...props} pagination={<Pagination />} />
|
|
195
196
|
</MainCard>
|
|
196
197
|
);
|
|
197
198
|
}
|
package/src/playground/App.jsx
CHANGED
|
@@ -9,7 +9,7 @@ import { ApplicaDataProvider, createAttachmentsParser } from '@applica-software-
|
|
|
9
9
|
import { ApplicaAuthProvider, LocalStorage } from '@applica-software-guru/iam-client';
|
|
10
10
|
import { CustomRoutes } from 'ra-core';
|
|
11
11
|
import { Route } from 'react-router-dom';
|
|
12
|
-
import { createJsonI18nProvider } from '@/i18n';
|
|
12
|
+
import { createI18nProvider, createJsonI18nProvider } from '@/i18n';
|
|
13
13
|
|
|
14
14
|
const authProvider = new ApplicaAuthProvider(API_URL, new LocalStorage());
|
|
15
15
|
const dataProvider = new ApplicaDataProvider({
|
|
@@ -20,7 +20,7 @@ const dataProvider = new ApplicaDataProvider({
|
|
|
20
20
|
attachmentsParser: createAttachmentsParser(),
|
|
21
21
|
HttpErrorClass: HttpError
|
|
22
22
|
});
|
|
23
|
-
const i18nProvider =
|
|
23
|
+
const i18nProvider = createI18nProvider({ apiUrl: API_URL, allowMissing: true, defaultLocale: 'it' });
|
|
24
24
|
function App() {
|
|
25
25
|
return (
|
|
26
26
|
<ApplicaAdmin
|