foris 6.6.2 → 6.7.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.
@@ -34,12 +34,14 @@ const Input = forwardRef(
34
34
 
35
35
  return (
36
36
  <div className="mb-3">
37
- <label
38
- className={`form-label ${labelClassName || ""}`.trim()}
39
- htmlFor={uid}
40
- >
41
- {label}
42
- </label>
37
+ {label && (
38
+ <label
39
+ className={`form-label ${labelClassName || ""}`.trim()}
40
+ htmlFor={uid}
41
+ >
42
+ {label}
43
+ </label>
44
+ )}
43
45
  <div className={`input-group ${groupClassName || ""}`.trim()}>
44
46
  <input
45
47
  className={`form-control ${inputClassName}`.trim()}
@@ -65,7 +67,7 @@ Input.displayName = "Input";
65
67
 
66
68
  Input.propTypes = {
67
69
  type: PropTypes.string.isRequired,
68
- label: PropTypes.string.isRequired,
70
+ label: PropTypes.string,
69
71
  helpText: PropTypes.string,
70
72
  error: PropTypes.string,
71
73
  className: PropTypes.string,
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
2
+ * Copyright (C) 2019-2025 CZ.NIC z.s.p.o. (https://www.nic.cz/)
3
3
  *
4
4
  * This is free software, licensed under the GNU General Public License v3.
5
5
  * See /LICENSE for more information.
@@ -9,7 +9,7 @@ import React, { useState, useEffect } from "react";
9
9
 
10
10
  import PropTypes from "prop-types";
11
11
 
12
- import { useAPIPost } from "../../api/hooks";
12
+ import { useAPIPost, useAPIPut } from "../../api/hooks";
13
13
  import { API_STATE } from "../../api/utils";
14
14
  import Button from "../../bootstrap/Button";
15
15
  import {
@@ -23,6 +23,8 @@ import { useAlert } from "../../context/alertContext/AlertContext";
23
23
  ActionButtonWithModal.propTypes = {
24
24
  /** Component that triggers the action. */
25
25
  actionTrigger: PropTypes.elementType.isRequired,
26
+ /** Method to use for the action. */
27
+ actionMethod: PropTypes.string,
26
28
  /** URL to send the action to. */
27
29
  actionUrl: PropTypes.string.isRequired,
28
30
  /** Title of the modal. */
@@ -41,6 +43,7 @@ ActionButtonWithModal.propTypes = {
41
43
 
42
44
  function ActionButtonWithModal({
43
45
  actionTrigger: ActionTriggerComponent,
46
+ actionMethod = "POST",
44
47
  actionUrl,
45
48
  modalTitle,
46
49
  modalMessage,
@@ -51,24 +54,43 @@ function ActionButtonWithModal({
51
54
  }) {
52
55
  const [triggered, setTriggered] = useState(false);
53
56
  const [modalShown, setModalShown] = useState(false);
54
- const [triggerActionStatus, triggerAction] = useAPIPost(actionUrl);
57
+ const [triggerPostActionStatus, triggerPostAction] = useAPIPost(actionUrl);
58
+ const [triggerPutActionStatus, triggerPutAction] = useAPIPut(actionUrl);
55
59
 
56
60
  const [setAlert] = useAlert();
57
61
  useEffect(() => {
58
- if (triggerActionStatus.state === API_STATE.SUCCESS) {
62
+ if (
63
+ triggerPostActionStatus.state === API_STATE.SUCCESS ||
64
+ triggerPutActionStatus.state === API_STATE.SUCCESS
65
+ ) {
59
66
  setAlert(
60
67
  successMessage || _("Action successful."),
61
68
  API_STATE.SUCCESS
62
69
  );
70
+ setTriggered(false);
63
71
  }
64
- if (triggerActionStatus.state === API_STATE.ERROR) {
72
+ if (
73
+ triggerPostActionStatus.state === API_STATE.ERROR ||
74
+ triggerPutActionStatus.state === API_STATE.ERROR
75
+ ) {
65
76
  setAlert(errorMessage || _("Action failed."));
77
+ setTriggered(false);
66
78
  }
67
- }, [triggerActionStatus, setAlert, successMessage, errorMessage]);
79
+ }, [
80
+ triggerPostActionStatus,
81
+ triggerPutActionStatus,
82
+ setAlert,
83
+ successMessage,
84
+ errorMessage,
85
+ ]);
68
86
 
69
87
  const actionHandler = () => {
70
88
  setTriggered(true);
71
- triggerAction();
89
+ if (actionMethod === "POST") {
90
+ triggerPostAction();
91
+ } else {
92
+ triggerPutAction();
93
+ }
72
94
  setModalShown(false);
73
95
  };
74
96
 
@@ -7,18 +7,22 @@
7
7
 
8
8
  import React, { useMemo, useState } from "react";
9
9
 
10
+ import { rankItem } from "@tanstack/match-sorter-utils";
10
11
  import {
11
12
  flexRender,
12
13
  getCoreRowModel,
13
14
  getSortedRowModel,
15
+ getFilteredRowModel,
14
16
  getPaginationRowModel,
15
17
  useReactTable,
16
18
  } from "@tanstack/react-table";
17
19
  import PropTypes from "prop-types";
18
20
 
19
21
  import RichTableBody from "./RichTableBody";
22
+ import RichTableColumnsDropdown from "./RichTableColumnsDropdown";
20
23
  import RichTableHeader from "./RichTableHeader";
21
24
  import RichTablePagination from "./RichTablePagination";
25
+ import Input from "../../bootstrap/Input";
22
26
 
23
27
  RichTable.propTypes = {
24
28
  /** Columns to be displayed in the table */
@@ -46,36 +50,69 @@ export default function RichTable({
46
50
  pageIndex,
47
51
  pageSize,
48
52
  });
53
+ const [globalFilter, setGlobalFilter] = useState("");
54
+ const [columnVisibility, setColumnVisibility] = useState({});
49
55
 
50
56
  const table = useReactTable({
51
57
  data,
52
58
  columns: tableColumns,
59
+ filterFns: {
60
+ fuzzy: fuzzyFilter,
61
+ },
62
+ globalFilterFn: "fuzzy",
53
63
  getCoreRowModel: getCoreRowModel(),
54
64
  getSortedRowModel: getSortedRowModel(),
55
65
  getPaginationRowModel: getPaginationRowModel(),
56
- onPaginationChange: setPagination,
66
+ getFilteredRowModel: getFilteredRowModel(),
57
67
  onSortingChange: setSorting,
68
+ onPaginationChange: setPagination,
69
+ onGlobalFilterChange: setGlobalFilter,
70
+ onColumnVisibilityChange: setColumnVisibility,
58
71
  state: {
59
72
  sorting,
60
73
  pagination,
74
+ globalFilter,
75
+ columnVisibility,
61
76
  },
62
77
  });
63
78
 
64
79
  const paginationIsNeeded = data.length > pageSize && withPagination;
65
80
 
66
81
  return (
67
- <div className="table-responsive">
68
- <table className="table table-hover text-nowrap">
69
- <RichTableHeader table={table} flexRender={flexRender} />
70
- <RichTableBody table={table} flexRender={flexRender} />
71
- </table>
72
- {paginationIsNeeded && (
73
- <RichTablePagination
74
- table={table}
75
- tablePageSize={pageSize}
76
- allRows={data.length}
82
+ <div>
83
+ <div className="d-flex justify-content-between align-items-center">
84
+ <Input
85
+ className="me-3"
86
+ type="text"
87
+ placeholder={_("Search…")}
88
+ value={globalFilter ?? ""}
89
+ onChange={(e) => setGlobalFilter(String(e.target.value))}
77
90
  />
78
- )}
91
+ <RichTableColumnsDropdown columns={table.getAllLeafColumns()} />
92
+ </div>
93
+ <div className="table-responsive">
94
+ <table className="table table-hover text-nowrap">
95
+ <RichTableHeader table={table} flexRender={flexRender} />
96
+ <RichTableBody
97
+ table={table}
98
+ columns={tableColumns}
99
+ flexRender={flexRender}
100
+ />
101
+ </table>
102
+ {paginationIsNeeded && (
103
+ <RichTablePagination
104
+ table={table}
105
+ tablePageSize={pageSize}
106
+ allRows={data.length}
107
+ />
108
+ )}
109
+ </div>
79
110
  </div>
80
111
  );
81
112
  }
113
+
114
+ function fuzzyFilter(row, columnId, value, addMeta) {
115
+ const itemRank = rankItem(row.getValue(columnId), value);
116
+ addMeta({ itemRank });
117
+ return itemRank.passed;
118
+ }
@@ -13,34 +13,44 @@ RichTableBody.propTypes = {
13
13
  table: propTypes.shape({
14
14
  getRowModel: propTypes.func.isRequired,
15
15
  }).isRequired,
16
+ columns: propTypes.array.isRequired,
16
17
  flexRender: propTypes.func.isRequired,
17
18
  };
18
19
 
19
- function RichTableBody({ table, flexRender }) {
20
+ function RichTableBody({ table, columns, flexRender }) {
20
21
  return (
21
22
  <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
- })}
23
+ {table.getRowModel().rows?.length ? (
24
+ table.getRowModel().rows.map((row) => {
25
+ return (
26
+ <tr key={row.id} className="align-middle">
27
+ {row.getVisibleCells().map((cell) => {
28
+ return (
29
+ <td
30
+ key={cell.id}
31
+ {...(cell.column.columnDef
32
+ .className && {
33
+ className:
34
+ cell.column.columnDef.className,
35
+ })}
36
+ >
37
+ {flexRender(
38
+ cell.column.columnDef.cell,
39
+ cell.getContext()
40
+ )}
41
+ </td>
42
+ );
43
+ })}
44
+ </tr>
45
+ );
46
+ })
47
+ ) : (
48
+ <tr>
49
+ <td colSpan={columns.length} className="text-center py-4">
50
+ <span>{_("No results.")}</span>
51
+ </td>
52
+ </tr>
53
+ )}
44
54
  </tbody>
45
55
  );
46
56
  }
@@ -0,0 +1,90 @@
1
+ /*
2
+ * Copyright (C) 2019-2025 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 { faCheck, faRotateLeft } from "@fortawesome/free-solid-svg-icons";
11
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
12
+ import PropTypes from "prop-types";
13
+
14
+ import Button from "../../bootstrap/Button";
15
+
16
+ RichTableColumnsDropdown.propTypes = {
17
+ columns: PropTypes.array.isRequired,
18
+ };
19
+
20
+ function RichTableColumnsDropdown({ columns }) {
21
+ return (
22
+ <div className="dropdown mb-3">
23
+ <Button
24
+ className="btn btn-outline-secondary dropdown-toggle"
25
+ data-bs-toggle="dropdown"
26
+ >
27
+ {_("Columns")}
28
+ </Button>
29
+ <ul className="dropdown-menu dropdown-menu-end">
30
+ {columns.map((column) => {
31
+ return (
32
+ <li key={column.id}>
33
+ <button
34
+ type="button"
35
+ className="dropdown-item d-flex align-items-center"
36
+ onClick={column.getToggleVisibilityHandler()}
37
+ style={{ paddingLeft: "2rem" }}
38
+ disabled={
39
+ column.columnDef?.enableHiding === false
40
+ }
41
+ >
42
+ {column.getIsVisible() && (
43
+ <FontAwesomeIcon
44
+ icon={faCheck}
45
+ className="position-absolute text-secondary me-2"
46
+ style={{ left: "0.6rem" }}
47
+ width="1rem"
48
+ />
49
+ )}
50
+ <span>{column.columnDef.header}</span>
51
+ </button>
52
+ </li>
53
+ );
54
+ })}
55
+ {columns.some((column) => !column.getIsVisible()) && (
56
+ <>
57
+ <li>
58
+ <hr className="dropdown-divider" />
59
+ </li>
60
+ <li>
61
+ <button
62
+ type="button"
63
+ className="dropdown-item d-flex align-items-center"
64
+ style={{ paddingLeft: "2rem" }}
65
+ onClick={() => {
66
+ // toggleVisibility for columns that are hidden
67
+ columns.forEach((column) => {
68
+ if (!column.getIsVisible()) {
69
+ column.toggleVisibility();
70
+ }
71
+ });
72
+ }}
73
+ >
74
+ <FontAwesomeIcon
75
+ icon={faRotateLeft}
76
+ className="position-absolute text-secondary me-2"
77
+ width="1rem"
78
+ style={{ left: "0.6rem" }}
79
+ />
80
+ {_("Reset")}
81
+ </button>
82
+ </li>
83
+ </>
84
+ )}
85
+ </ul>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ export default RichTableColumnsDropdown;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
2
+ * Copyright (C) 2019-2025 CZ.NIC z.s.p.o. (https://www.nic.cz/)
3
3
  *
4
4
  * This is free software, licensed under the GNU General Public License v3.
5
5
  * See /LICENSE for more information.
@@ -32,7 +32,7 @@ function RichTableHeader({ table, flexRender }) {
32
32
  };
33
33
 
34
34
  return (
35
- <thead className="thead-light">
35
+ <thead className="table-light">
36
36
  {table.getHeaderGroups().map((headerGroup) => (
37
37
  <tr key={headerGroup.id} role="row">
38
38
  {headerGroup.headers.map((header) => (
@@ -55,6 +55,12 @@ function RichTableHeader({ table, flexRender }) {
55
55
  ) : (
56
56
  <button
57
57
  type="button"
58
+ style={
59
+ header.column.columnDef
60
+ .headerClassName === "text-center"
61
+ ? { justifySelf: "center" }
62
+ : {}
63
+ }
58
64
  className={`btn btn-link text-decoration-none text-reset fw-bold p-0 d-flex align-items-center
59
65
  ${
60
66
  header.column.getCanSort()
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
2
+ * Copyright (C) 2019-2025 CZ.NIC z.s.p.o. (https://www.nic.cz/)
3
3
  *
4
4
  * This is free software, licensed under the GNU General Public License v3.
5
5
  * See /LICENSE for more information.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foris",
3
- "version": "6.6.2",
3
+ "version": "6.7.0",
4
4
  "description": "Foris JS library is a set of components and utils for reForis application and plugins.",
5
5
  "author": "CZ.NIC, z.s.p.o.",
6
6
  "repository": {
@@ -14,17 +14,18 @@
14
14
  "license": "GPL-3.0",
15
15
  "main": "./index.js",
16
16
  "dependencies": {
17
- "@fortawesome/fontawesome-svg-core": "^6.6.0",
18
- "@fortawesome/free-regular-svg-icons": "^6.6.0",
19
- "@fortawesome/free-solid-svg-icons": "^6.6.0",
17
+ "@fortawesome/fontawesome-svg-core": "^6.7.2",
18
+ "@fortawesome/free-regular-svg-icons": "^6.7.2",
19
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
20
20
  "@fortawesome/react-fontawesome": "^0.2.2",
21
- "@tanstack/react-table": "^8.20.5",
22
- "axios": "^1.7.2",
21
+ "@tanstack/match-sorter-utils": "^8.19.4",
22
+ "@tanstack/react-table": "^8.21.2",
23
+ "axios": "^1.7.9",
23
24
  "immutability-helper": "^3.1.1",
24
25
  "moment": "^2.30.1",
25
- "qrcode.react": "^3.1.0",
26
- "react-datetime": "^3.2.0",
27
- "react-uid": "^2.3.3"
26
+ "qrcode.react": "^4.2.0",
27
+ "react-datetime": "^3.3.1",
28
+ "react-uid": "^2.4.0"
28
29
  },
29
30
  "peerDependencies": {
30
31
  "bootstrap": "^5.3.3",
@@ -34,32 +35,32 @@
34
35
  "react-router-dom": "^5.1.2"
35
36
  },
36
37
  "devDependencies": {
37
- "@babel/cli": "^7.24.7",
38
- "@babel/core": "^7.24.7",
39
- "@babel/plugin-transform-runtime": "^7.24.7",
40
- "@babel/preset-env": "^7.24.7",
41
- "@babel/preset-react": "^7.24.7",
42
- "@testing-library/react": "^8.0.9",
43
- "babel-loader": "^8.1.0",
38
+ "@babel/cli": "^7.26.4",
39
+ "@babel/core": "^7.26.9",
40
+ "@babel/plugin-transform-runtime": "^7.26.9",
41
+ "@babel/preset-env": "^7.26.9",
42
+ "@babel/preset-react": "^7.26.3",
43
+ "@testing-library/react": "^12.1.5",
44
+ "babel-loader": "^9.2.1",
44
45
  "babel-polyfill": "^6.26.0",
45
46
  "bootstrap": "^5.3.3",
46
- "css-loader": "^5.2.4",
47
+ "css-loader": "^7.1.2",
47
48
  "eslint": "^8.57.0",
48
- "eslint-config-reforis": "^2.1.1",
49
+ "eslint-config-reforis": "^2.2.1",
49
50
  "file-loader": "^6.0.0",
50
51
  "jest": "^29.7.0",
51
52
  "jest-environment-jsdom": "^29.7.0",
52
- "jest-mock-axios": "^4.7.3",
53
- "moment-timezone": "^0.5.45",
54
- "prettier": "^3.3.2",
53
+ "jest-mock-axios": "^4.8.0",
54
+ "moment-timezone": "^0.5.47",
55
+ "prettier": "^3.5.3",
55
56
  "prop-types": "15.8.1",
56
57
  "react": "16.9.0",
57
58
  "react-dom": "16.9.0",
58
59
  "react-router-dom": "^5.1.2",
59
60
  "react-styleguidist": "^12.0.1",
60
61
  "snapshot-diff": "^0.10.0",
61
- "style-loader": "^1.2.1",
62
- "webpack": "^5.92.1"
62
+ "style-loader": "^4.0.0",
63
+ "webpack": "^5.98.0"
63
64
  },
64
65
  "scripts": {
65
66
  "lint": "eslint src",
package/foris-6.6.2.tgz DELETED
Binary file