foris 6.4.0 → 6.5.0-beta.a63b5bfa
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/bootstrap/Modal.js +10 -7
- package/common/ActionButtonWithModal/ActionButtonWithModal.js +135 -0
- package/common/ActionButtonWithModal/ActionButtonWithModal.md +39 -0
- package/common/RichTable/RichTable.js +86 -0
- package/common/RichTable/RichTable.md +135 -0
- package/common/RichTable/RichTableBody.js +48 -0
- package/common/RichTable/RichTableHeader.js +96 -0
- package/common/RichTable/RichTablePagination.js +128 -0
- package/foris-6.5.0.tgz +0 -0
- package/index.js +2 -1
- package/package.json +2 -1
- package/testUtils/customTestRender.js +1 -1
- package/translations/cs/LC_MESSAGES/forisjs.po +79 -21
- package/translations/da/LC_MESSAGES/forisjs.po +75 -16
- package/translations/de/LC_MESSAGES/forisjs.po +79 -21
- package/translations/el/LC_MESSAGES/forisjs.po +76 -19
- package/translations/en/LC_MESSAGES/forisjs.po +75 -16
- package/translations/es/LC_MESSAGES/forisjs.po +79 -21
- package/translations/fi/LC_MESSAGES/forisjs.po +75 -16
- package/translations/fo/LC_MESSAGES/forisjs.po +75 -16
- package/translations/forisjs.pot +61 -17
- package/translations/fr/LC_MESSAGES/forisjs.po +79 -21
- package/translations/hr/LC_MESSAGES/forisjs.po +75 -16
- package/translations/hu/LC_MESSAGES/forisjs.po +76 -16
- package/translations/it/LC_MESSAGES/forisjs.po +75 -16
- package/translations/ja/LC_MESSAGES/forisjs.po +76 -17
- package/translations/ko/LC_MESSAGES/forisjs.po +75 -16
- package/translations/lt/LC_MESSAGES/forisjs.po +75 -16
- package/translations/nb_NO/LC_MESSAGES/forisjs.po +78 -21
- package/translations/nl/LC_MESSAGES/forisjs.po +78 -22
- package/translations/pl/LC_MESSAGES/forisjs.po +79 -21
- package/translations/pt_BR/LC_MESSAGES/forisjs.po +77 -20
- package/translations/ro/LC_MESSAGES/forisjs.po +75 -16
- package/translations/ru/LC_MESSAGES/forisjs.po +79 -21
- package/translations/sk/LC_MESSAGES/forisjs.po +79 -21
- package/translations/sv/LC_MESSAGES/forisjs.po +78 -21
- package/common/RebootButton.js +0 -80
- package/foris-6.4.0.tgz +0 -0
- /package/testUtils/{cutomizationContextMock.js → customizationContextMock.js} +0 -0
package/bootstrap/Modal.js
CHANGED
|
@@ -88,18 +88,21 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
|
|
|
88
88
|
ModalHeader.propTypes = {
|
|
89
89
|
setShown: PropTypes.func.isRequired,
|
|
90
90
|
title: PropTypes.string.isRequired,
|
|
91
|
+
showCloseButton: PropTypes.bool,
|
|
91
92
|
};
|
|
92
93
|
|
|
93
|
-
export function ModalHeader({ setShown, title }) {
|
|
94
|
+
export function ModalHeader({ setShown, title, showCloseButton = true }) {
|
|
94
95
|
return (
|
|
95
96
|
<div className="modal-header">
|
|
96
97
|
<h1 className="modal-title fs-5">{title}</h1>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
{showCloseButton && (
|
|
99
|
+
<button
|
|
100
|
+
type="button"
|
|
101
|
+
className="btn-close"
|
|
102
|
+
onClick={() => setShown(false)}
|
|
103
|
+
aria-label={_("Close")}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
103
106
|
</div>
|
|
104
107
|
);
|
|
105
108
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
|
3
|
+
*
|
|
4
|
+
* This is free software, licensed under the GNU General Public License v3.
|
|
5
|
+
* See /LICENSE for more information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useEffect } from "react";
|
|
9
|
+
|
|
10
|
+
import PropTypes from "prop-types";
|
|
11
|
+
|
|
12
|
+
import { useAPIPost } from "../../api/hooks";
|
|
13
|
+
import { API_STATE } from "../../api/utils";
|
|
14
|
+
import Button from "../../bootstrap/Button";
|
|
15
|
+
import {
|
|
16
|
+
Modal,
|
|
17
|
+
ModalHeader,
|
|
18
|
+
ModalBody,
|
|
19
|
+
ModalFooter,
|
|
20
|
+
} from "../../bootstrap/Modal";
|
|
21
|
+
import { useAlert } from "../../context/alertContext/AlertContext";
|
|
22
|
+
|
|
23
|
+
ActionButtonWithModal.propTypes = {
|
|
24
|
+
/** Component that triggers the action. */
|
|
25
|
+
actionTrigger: PropTypes.elementType.isRequired,
|
|
26
|
+
/** URL to send the action to. */
|
|
27
|
+
actionUrl: PropTypes.string.isRequired,
|
|
28
|
+
/** Title of the modal. */
|
|
29
|
+
modalTitle: PropTypes.string.isRequired,
|
|
30
|
+
/** Message of the modal. */
|
|
31
|
+
modalMessage: PropTypes.string.isRequired,
|
|
32
|
+
/** Text of the action button in the modal. */
|
|
33
|
+
modalActionText: PropTypes.string,
|
|
34
|
+
/** Props for the action button in the modal. */
|
|
35
|
+
modalActionProps: PropTypes.object,
|
|
36
|
+
/** Message to display on successful action. */
|
|
37
|
+
successMessage: PropTypes.string,
|
|
38
|
+
/** Message to display on failed action. */
|
|
39
|
+
errorMessage: PropTypes.string,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function ActionButtonWithModal({
|
|
43
|
+
actionTrigger: ActionTriggerComponent,
|
|
44
|
+
actionUrl,
|
|
45
|
+
modalTitle,
|
|
46
|
+
modalMessage,
|
|
47
|
+
modalActionText,
|
|
48
|
+
modalActionProps,
|
|
49
|
+
successMessage,
|
|
50
|
+
errorMessage,
|
|
51
|
+
}) {
|
|
52
|
+
const [triggered, setTriggered] = useState(false);
|
|
53
|
+
const [modalShown, setModalShown] = useState(false);
|
|
54
|
+
const [triggerActionStatus, triggerAction] = useAPIPost(actionUrl);
|
|
55
|
+
|
|
56
|
+
const [setAlert] = useAlert();
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (triggerActionStatus.state === API_STATE.SUCCESS) {
|
|
59
|
+
setAlert(
|
|
60
|
+
successMessage || _("Action successful."),
|
|
61
|
+
API_STATE.SUCCESS
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (triggerActionStatus.state === API_STATE.ERROR) {
|
|
65
|
+
setAlert(errorMessage || _("Action failed."));
|
|
66
|
+
}
|
|
67
|
+
}, [triggerActionStatus, setAlert, successMessage, errorMessage]);
|
|
68
|
+
|
|
69
|
+
const actionHandler = () => {
|
|
70
|
+
setTriggered(true);
|
|
71
|
+
triggerAction();
|
|
72
|
+
setModalShown(false);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<ActionModal
|
|
78
|
+
shown={modalShown}
|
|
79
|
+
setShown={setModalShown}
|
|
80
|
+
onAction={actionHandler}
|
|
81
|
+
title={modalTitle}
|
|
82
|
+
message={modalMessage}
|
|
83
|
+
actionText={modalActionText}
|
|
84
|
+
actionProps={modalActionProps}
|
|
85
|
+
/>
|
|
86
|
+
<ActionTriggerComponent
|
|
87
|
+
loading={triggered}
|
|
88
|
+
disabled={triggered}
|
|
89
|
+
onClick={() => setModalShown(true)}
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ActionModal.propTypes = {
|
|
96
|
+
shown: PropTypes.bool.isRequired,
|
|
97
|
+
setShown: PropTypes.func.isRequired,
|
|
98
|
+
onAction: PropTypes.func.isRequired,
|
|
99
|
+
title: PropTypes.string.isRequired,
|
|
100
|
+
message: PropTypes.string.isRequired,
|
|
101
|
+
actionText: PropTypes.string,
|
|
102
|
+
actionProps: PropTypes.object,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
function ActionModal({
|
|
106
|
+
shown,
|
|
107
|
+
setShown,
|
|
108
|
+
onAction,
|
|
109
|
+
title,
|
|
110
|
+
message,
|
|
111
|
+
actionText,
|
|
112
|
+
actionProps,
|
|
113
|
+
}) {
|
|
114
|
+
return (
|
|
115
|
+
<Modal shown={shown} setShown={setShown}>
|
|
116
|
+
<ModalHeader setShown={setShown} title={title} />
|
|
117
|
+
<ModalBody>
|
|
118
|
+
<p className="mb-0">{message}</p>
|
|
119
|
+
</ModalBody>
|
|
120
|
+
<ModalFooter>
|
|
121
|
+
<Button
|
|
122
|
+
className="btn-secondary"
|
|
123
|
+
onClick={() => setShown(false)}
|
|
124
|
+
>
|
|
125
|
+
{_("Cancel")}
|
|
126
|
+
</Button>
|
|
127
|
+
<Button onClick={onAction} {...actionProps}>
|
|
128
|
+
{actionText || _("Confirm")}
|
|
129
|
+
</Button>
|
|
130
|
+
</ModalFooter>
|
|
131
|
+
</Modal>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default ActionButtonWithModal;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
RebootButton component is a button that opens a modal dialog to confirm the
|
|
2
|
+
reboot of the device.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
```jsx
|
|
7
|
+
import React, { useEffect, createContext } from "react";
|
|
8
|
+
|
|
9
|
+
import Button from "../../bootstrap/Button";
|
|
10
|
+
import { AlertContextProvider } from "../../context/alertContext/AlertContext";
|
|
11
|
+
import ActionButtonWithModal from "./ActionButtonWithModal";
|
|
12
|
+
|
|
13
|
+
window.AlertContext = React.createContext();
|
|
14
|
+
|
|
15
|
+
const RebootButtonExample = () => {
|
|
16
|
+
const ActionButton = (props) => {
|
|
17
|
+
return <Button {...props}>Action</Button>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<AlertContextProvider>
|
|
22
|
+
<div id="modal-container" />
|
|
23
|
+
<div id="alert-container" />
|
|
24
|
+
<ActionButtonWithModal
|
|
25
|
+
actionTrigger={ActionButton}
|
|
26
|
+
actionUrl="/reforis/api/action"
|
|
27
|
+
modalTitle="Warning!"
|
|
28
|
+
modalMessage="Are you sure you want to perform this action?"
|
|
29
|
+
modalActionText="Confirm action"
|
|
30
|
+
modalActionProps={{ className: "btn-danger" }}
|
|
31
|
+
successMessage="Action request succeeded."
|
|
32
|
+
errorMessage="Action request failed."
|
|
33
|
+
/>
|
|
34
|
+
</AlertContextProvider>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
<RebootButtonExample />;
|
|
39
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
|
3
|
+
*
|
|
4
|
+
* This is free software, licensed under the GNU General Public License v3.
|
|
5
|
+
* See /LICENSE for more information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useMemo, useState } from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
flexRender,
|
|
12
|
+
getCoreRowModel,
|
|
13
|
+
getSortedRowModel,
|
|
14
|
+
getPaginationRowModel,
|
|
15
|
+
useReactTable,
|
|
16
|
+
} from "@tanstack/react-table";
|
|
17
|
+
import PropTypes from "prop-types";
|
|
18
|
+
|
|
19
|
+
import RichTableBody from "./RichTableBody";
|
|
20
|
+
import RichTableHeader from "./RichTableHeader";
|
|
21
|
+
import RichTablePagination from "./RichTablePagination";
|
|
22
|
+
|
|
23
|
+
const fallbackData = [];
|
|
24
|
+
|
|
25
|
+
RichTable.propTypes = {
|
|
26
|
+
/** Columns to be displayed in the table */
|
|
27
|
+
columns: PropTypes.array.isRequired,
|
|
28
|
+
/** Data to be displayed in the table */
|
|
29
|
+
data: PropTypes.array.isRequired,
|
|
30
|
+
/** Whether to display pagination */
|
|
31
|
+
withPagination: PropTypes.bool,
|
|
32
|
+
/** Number of rows per page */
|
|
33
|
+
pageSize: PropTypes.number,
|
|
34
|
+
/** Index of the current page */
|
|
35
|
+
pageIndex: PropTypes.number,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function RichTable({
|
|
39
|
+
columns,
|
|
40
|
+
data,
|
|
41
|
+
withPagination,
|
|
42
|
+
pageSize = 5,
|
|
43
|
+
pageIndex = 0,
|
|
44
|
+
}) {
|
|
45
|
+
const tableColumns = useMemo(() => columns, [columns]);
|
|
46
|
+
const [tableData] = useState(data ?? fallbackData);
|
|
47
|
+
const [sorting, setSorting] = useState([]);
|
|
48
|
+
const [pagination, setPagination] = useState({
|
|
49
|
+
pageIndex,
|
|
50
|
+
pageSize,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const table = useReactTable({
|
|
54
|
+
data: tableData,
|
|
55
|
+
columns: tableColumns,
|
|
56
|
+
getCoreRowModel: getCoreRowModel(),
|
|
57
|
+
getSortedRowModel: getSortedRowModel(),
|
|
58
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
59
|
+
onPaginationChange: setPagination,
|
|
60
|
+
onSortingChange: setSorting,
|
|
61
|
+
state: {
|
|
62
|
+
sorting,
|
|
63
|
+
pagination,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const paginationIsNeeded = tableData.length > pageSize && withPagination;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="table-responsive">
|
|
71
|
+
<table className="table table-hover text-nowrap">
|
|
72
|
+
<RichTableHeader table={table} flexRender={flexRender} />
|
|
73
|
+
<RichTableBody table={table} flexRender={flexRender} />
|
|
74
|
+
</table>
|
|
75
|
+
{paginationIsNeeded && (
|
|
76
|
+
<RichTablePagination
|
|
77
|
+
table={table}
|
|
78
|
+
tablePageSize={pageSize}
|
|
79
|
+
allRows={tableData.length}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default RichTable;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
### Description
|
|
2
|
+
|
|
3
|
+
Rich Table is a table component based on
|
|
4
|
+
[Tanstack React Table](https://tanstack.com/table/). It adds some features to
|
|
5
|
+
the table component, such as:
|
|
6
|
+
|
|
7
|
+
- **Pagination**: The table can be paginated.
|
|
8
|
+
- **Sorting**: The table can be sorted by columns.
|
|
9
|
+
- **Row Expansion**: The table rows can be expanded. (To be implemented)
|
|
10
|
+
|
|
11
|
+
### Example
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import RichTable from "./RichTable";
|
|
15
|
+
|
|
16
|
+
const columns = [
|
|
17
|
+
{
|
|
18
|
+
header: "Name",
|
|
19
|
+
accessorKey: "name",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
header: "Surname",
|
|
23
|
+
accessorKey: "surname",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
header: "Age",
|
|
27
|
+
accessorKey: "age",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
header: "Phone",
|
|
31
|
+
accessorKey: "phone",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const data = [
|
|
36
|
+
{
|
|
37
|
+
name: "John",
|
|
38
|
+
surname: "Coltrane",
|
|
39
|
+
age: 30,
|
|
40
|
+
phone: "123456789",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "Jane",
|
|
44
|
+
surname: "Doe",
|
|
45
|
+
age: 25,
|
|
46
|
+
phone: "987654321",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Alice",
|
|
50
|
+
surname: "Smith",
|
|
51
|
+
age: 35,
|
|
52
|
+
phone: "123456789",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "Bob",
|
|
56
|
+
surname: "Smith",
|
|
57
|
+
age: 40,
|
|
58
|
+
phone: "987654321",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "Charlie",
|
|
62
|
+
surname: "Brown",
|
|
63
|
+
age: 45,
|
|
64
|
+
phone: "123456789",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "Daisy",
|
|
68
|
+
surname: "Brown",
|
|
69
|
+
age: 50,
|
|
70
|
+
phone: "987654321",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "Eve",
|
|
74
|
+
surname: "Johnson",
|
|
75
|
+
age: 55,
|
|
76
|
+
phone: "123456789",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "Frank",
|
|
80
|
+
surname: "Johnson",
|
|
81
|
+
age: 60,
|
|
82
|
+
phone: "987654321",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "Grace",
|
|
86
|
+
surname: "Williams",
|
|
87
|
+
age: 65,
|
|
88
|
+
phone: "123456789",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "Henry",
|
|
92
|
+
surname: "Williams",
|
|
93
|
+
age: 70,
|
|
94
|
+
phone: "987654321",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Ivy",
|
|
98
|
+
surname: "Brown",
|
|
99
|
+
age: 75,
|
|
100
|
+
phone: "123456789",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Jack",
|
|
104
|
+
surname: "Brown",
|
|
105
|
+
age: 80,
|
|
106
|
+
phone: "987654321",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "Kelly",
|
|
110
|
+
surname: "Johnson",
|
|
111
|
+
age: 85,
|
|
112
|
+
phone: "123456789",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "Liam",
|
|
116
|
+
surname: "Johnson",
|
|
117
|
+
age: 90,
|
|
118
|
+
phone: "987654321",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "Mia",
|
|
122
|
+
surname: "Williams",
|
|
123
|
+
age: 95,
|
|
124
|
+
phone: "123456789",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "Nathan",
|
|
128
|
+
surname: "Williams",
|
|
129
|
+
age: 100,
|
|
130
|
+
phone: "987654321",
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
<RichTable columns={columns} data={data} withPagination />;
|
|
135
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
|
3
|
+
*
|
|
4
|
+
* This is free software, licensed under the GNU General Public License v3.
|
|
5
|
+
* See /LICENSE for more information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import propTypes from "prop-types";
|
|
11
|
+
|
|
12
|
+
RichTableBody.propTypes = {
|
|
13
|
+
table: propTypes.shape({
|
|
14
|
+
getRowModel: propTypes.func.isRequired,
|
|
15
|
+
}).isRequired,
|
|
16
|
+
flexRender: propTypes.func.isRequired,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function RichTableBody({ table, flexRender }) {
|
|
20
|
+
return (
|
|
21
|
+
<tbody>
|
|
22
|
+
{table.getRowModel().rows.map((row) => {
|
|
23
|
+
return (
|
|
24
|
+
<tr key={row.id} className="align-middle">
|
|
25
|
+
{row.getVisibleCells().map((cell) => {
|
|
26
|
+
return (
|
|
27
|
+
<td
|
|
28
|
+
key={cell.id}
|
|
29
|
+
{...(cell.column.columnDef.className && {
|
|
30
|
+
className:
|
|
31
|
+
cell.column.columnDef.className,
|
|
32
|
+
})}
|
|
33
|
+
>
|
|
34
|
+
{flexRender(
|
|
35
|
+
cell.column.columnDef.cell,
|
|
36
|
+
cell.getContext()
|
|
37
|
+
)}
|
|
38
|
+
</td>
|
|
39
|
+
);
|
|
40
|
+
})}
|
|
41
|
+
</tr>
|
|
42
|
+
);
|
|
43
|
+
})}
|
|
44
|
+
</tbody>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default RichTableBody;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
|
3
|
+
*
|
|
4
|
+
* This is free software, licensed under the GNU General Public License v3.
|
|
5
|
+
* See /LICENSE for more information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
faSquareCaretUp,
|
|
12
|
+
faSquareCaretDown,
|
|
13
|
+
} from "@fortawesome/free-solid-svg-icons";
|
|
14
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
15
|
+
import propTypes from "prop-types";
|
|
16
|
+
|
|
17
|
+
RichTableHeader.propTypes = {
|
|
18
|
+
table: propTypes.shape({
|
|
19
|
+
getHeaderGroups: propTypes.func.isRequired,
|
|
20
|
+
}).isRequired,
|
|
21
|
+
flexRender: propTypes.func.isRequired,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function RichTableHeader({ table, flexRender }) {
|
|
25
|
+
const getThTitle = (header) => {
|
|
26
|
+
if (!header.column.getCanSort()) return undefined;
|
|
27
|
+
|
|
28
|
+
const nextSortingOrder = header.column.getNextSortingOrder();
|
|
29
|
+
if (nextSortingOrder === "asc") return _("Sort ascending");
|
|
30
|
+
if (nextSortingOrder === "desc") return _("Sort descending");
|
|
31
|
+
return _("Clear sort");
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<thead className="thead-light">
|
|
36
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
37
|
+
<tr key={headerGroup.id} role="row">
|
|
38
|
+
{headerGroup.headers.map((header) => (
|
|
39
|
+
<th
|
|
40
|
+
key={header.id}
|
|
41
|
+
colSpan={header.colSpan}
|
|
42
|
+
{...(header.column.columnDef.headerClassName && {
|
|
43
|
+
className:
|
|
44
|
+
header.column.columnDef.headerClassName,
|
|
45
|
+
})}
|
|
46
|
+
>
|
|
47
|
+
{header.isPlaceholder ||
|
|
48
|
+
header.column.columnDef.headerIsHidden ? (
|
|
49
|
+
<div className="d-none" aria-hidden="true">
|
|
50
|
+
{flexRender(
|
|
51
|
+
header.column.columnDef.header,
|
|
52
|
+
header.getContext()
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
) : (
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className={`btn btn-link text-decoration-none text-reset fw-bold p-0 d-flex align-items-center
|
|
59
|
+
${
|
|
60
|
+
header.column.getCanSort()
|
|
61
|
+
? "d-flex align-items-center"
|
|
62
|
+
: ""
|
|
63
|
+
}
|
|
64
|
+
`}
|
|
65
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
66
|
+
title={getThTitle(header)}
|
|
67
|
+
>
|
|
68
|
+
{flexRender(
|
|
69
|
+
header.column.columnDef.header,
|
|
70
|
+
header.getContext()
|
|
71
|
+
)}
|
|
72
|
+
{{
|
|
73
|
+
asc: (
|
|
74
|
+
<FontAwesomeIcon
|
|
75
|
+
icon={faSquareCaretUp}
|
|
76
|
+
className="ms-1 text-primary"
|
|
77
|
+
/>
|
|
78
|
+
),
|
|
79
|
+
desc: (
|
|
80
|
+
<FontAwesomeIcon
|
|
81
|
+
icon={faSquareCaretDown}
|
|
82
|
+
className="ms-1 text-primary"
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
}[header.column.getIsSorted()] ?? null}
|
|
86
|
+
</button>
|
|
87
|
+
)}
|
|
88
|
+
</th>
|
|
89
|
+
))}
|
|
90
|
+
</tr>
|
|
91
|
+
))}
|
|
92
|
+
</thead>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default RichTableHeader;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
|
3
|
+
*
|
|
4
|
+
* This is free software, licensed under the GNU General Public License v3.
|
|
5
|
+
* See /LICENSE for more information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useMemo } from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
faAngleLeft,
|
|
12
|
+
faAnglesLeft,
|
|
13
|
+
faAngleRight,
|
|
14
|
+
faAnglesRight,
|
|
15
|
+
} from "@fortawesome/free-solid-svg-icons";
|
|
16
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
17
|
+
import propTypes from "prop-types";
|
|
18
|
+
|
|
19
|
+
RichTablePagination.propTypes = {
|
|
20
|
+
table: propTypes.shape({
|
|
21
|
+
getState: propTypes.func.isRequired,
|
|
22
|
+
getCanPreviousPage: propTypes.func.isRequired,
|
|
23
|
+
getCanNextPage: propTypes.func.isRequired,
|
|
24
|
+
firstPage: propTypes.func.isRequired,
|
|
25
|
+
previousPage: propTypes.func.isRequired,
|
|
26
|
+
nextPage: propTypes.func.isRequired,
|
|
27
|
+
lastPage: propTypes.func.isRequired,
|
|
28
|
+
setPageSize: propTypes.func.isRequired,
|
|
29
|
+
getPageCount: propTypes.func.isRequired,
|
|
30
|
+
}).isRequired,
|
|
31
|
+
tablePageSize: propTypes.number,
|
|
32
|
+
allRows: propTypes.number,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function RichTablePagination({ table, tablePageSize, allRows }) {
|
|
36
|
+
const { pagination } = table.getState();
|
|
37
|
+
const prevPagBtnDisabled = !table.getCanPreviousPage();
|
|
38
|
+
const nextPagBtnDisabled = !table.getCanNextPage();
|
|
39
|
+
|
|
40
|
+
const pageSizes = useMemo(() => {
|
|
41
|
+
return [tablePageSize ?? 5, 10, 25].filter(
|
|
42
|
+
(value, index, self) => self.indexOf(value) === index
|
|
43
|
+
);
|
|
44
|
+
}, [tablePageSize]);
|
|
45
|
+
|
|
46
|
+
const renderPaginationButton = (icon, ariaLabel, onClick, disabled) => (
|
|
47
|
+
<li
|
|
48
|
+
className={`page-item ${disabled ? "disabled" : ""}`}
|
|
49
|
+
style={{ cursor: disabled ? "not-allowed" : "pointer" }}
|
|
50
|
+
>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
className="page-link"
|
|
54
|
+
aria-label={ariaLabel}
|
|
55
|
+
onClick={onClick}
|
|
56
|
+
disabled={disabled}
|
|
57
|
+
>
|
|
58
|
+
<FontAwesomeIcon icon={icon} />
|
|
59
|
+
</button>
|
|
60
|
+
</li>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<nav
|
|
65
|
+
aria-label={_("Pagination navigation bar")}
|
|
66
|
+
className="d-flex gap-2 justify-content-start align-items-center mx-2 mb-1 text-nowrap"
|
|
67
|
+
>
|
|
68
|
+
<ul className="pagination pagination-sm mb-0">
|
|
69
|
+
{renderPaginationButton(
|
|
70
|
+
faAnglesLeft,
|
|
71
|
+
_("First page"),
|
|
72
|
+
() => table.firstPage(),
|
|
73
|
+
prevPagBtnDisabled
|
|
74
|
+
)}
|
|
75
|
+
{renderPaginationButton(
|
|
76
|
+
faAngleLeft,
|
|
77
|
+
_("Previous page"),
|
|
78
|
+
() => table.previousPage(),
|
|
79
|
+
prevPagBtnDisabled
|
|
80
|
+
)}
|
|
81
|
+
{renderPaginationButton(
|
|
82
|
+
faAngleRight,
|
|
83
|
+
_("Next page"),
|
|
84
|
+
() => table.nextPage(),
|
|
85
|
+
nextPagBtnDisabled
|
|
86
|
+
)}
|
|
87
|
+
{renderPaginationButton(
|
|
88
|
+
faAnglesRight,
|
|
89
|
+
_("Last page"),
|
|
90
|
+
() => table.lastPage(),
|
|
91
|
+
nextPagBtnDisabled
|
|
92
|
+
)}
|
|
93
|
+
</ul>
|
|
94
|
+
<span>
|
|
95
|
+
{_("Page")}
|
|
96
|
+
<span className="fw-bold">
|
|
97
|
+
{pagination.pageIndex + 1}
|
|
98
|
+
{_("of")}
|
|
99
|
+
{table.getPageCount().toLocaleString()}
|
|
100
|
+
</span>
|
|
101
|
+
</span>
|
|
102
|
+
<div
|
|
103
|
+
className="vr mx-1 align-self-center"
|
|
104
|
+
style={{ height: "1.5rem" }}
|
|
105
|
+
/>
|
|
106
|
+
<span>{_("Rows per page:")}</span>
|
|
107
|
+
<select
|
|
108
|
+
className="form-select form-select-sm w-auto"
|
|
109
|
+
aria-label={_("Select rows per page")}
|
|
110
|
+
value={pagination.pageSize}
|
|
111
|
+
onChange={(e) => {
|
|
112
|
+
table.setPageSize(Number(e.target.value));
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{pageSizes.map((pageSize) => (
|
|
116
|
+
<option key={pageSize} value={pageSize}>
|
|
117
|
+
{pageSize}
|
|
118
|
+
</option>
|
|
119
|
+
))}
|
|
120
|
+
<option key={allRows} value={allRows}>
|
|
121
|
+
{_("All")}
|
|
122
|
+
</option>
|
|
123
|
+
</select>
|
|
124
|
+
</nav>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default RichTablePagination;
|
package/foris-6.5.0.tgz
ADDED
|
Binary file
|