mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2025-04-19 08:06:40 +02:00
Compare commits
2 Commits
377b4279fd
...
0a839bf369
Author | SHA1 | Date | |
---|---|---|---|
|
0a839bf369 | ||
|
54a801a580 |
36
package-lock.json
generated
36
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
|
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
@ -3384,6 +3385,22 @@
|
|||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/match-sorter-utils": {
|
||||||
|
"version": "8.19.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
|
||||||
|
"integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"remove-accents": "0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/react-table": {
|
"node_modules/@tanstack/react-table": {
|
||||||
"version": "8.21.2",
|
"version": "8.21.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
|
||||||
@ -15865,6 +15882,12 @@
|
|||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/remove-accents": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/repeat-string": {
|
"node_modules/repeat-string": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||||
@ -20893,6 +20916,14 @@
|
|||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tanstack/match-sorter-utils": {
|
||||||
|
"version": "8.19.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
|
||||||
|
"integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
|
||||||
|
"requires": {
|
||||||
|
"remove-accents": "0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@tanstack/react-table": {
|
"@tanstack/react-table": {
|
||||||
"version": "8.21.2",
|
"version": "8.21.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
|
||||||
@ -30193,6 +30224,11 @@
|
|||||||
"mdast-util-to-markdown": "^0.6.0"
|
"mdast-util-to-markdown": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"remove-accents": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
|
||||||
|
},
|
||||||
"repeat-string": {
|
"repeat-string": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
|
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
@ -70,4 +71,4 @@
|
|||||||
"docs": "npx styleguidist build ",
|
"docs": "npx styleguidist build ",
|
||||||
"docs:watch": "styleguidist server"
|
"docs:watch": "styleguidist server"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,14 @@ const Input = forwardRef(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
{label && (
|
||||||
className={`form-label ${labelClassName || ""}`.trim()}
|
<label
|
||||||
htmlFor={uid}
|
className={`form-label ${labelClassName || ""}`.trim()}
|
||||||
>
|
htmlFor={uid}
|
||||||
{label}
|
>
|
||||||
</label>
|
{label}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
<div className={`input-group ${groupClassName || ""}`.trim()}>
|
<div className={`input-group ${groupClassName || ""}`.trim()}>
|
||||||
<input
|
<input
|
||||||
className={`form-control ${inputClassName}`.trim()}
|
className={`form-control ${inputClassName}`.trim()}
|
||||||
@ -65,7 +67,7 @@ Input.displayName = "Input";
|
|||||||
|
|
||||||
Input.propTypes = {
|
Input.propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string,
|
||||||
helpText: PropTypes.string,
|
helpText: PropTypes.string,
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
@ -7,18 +7,22 @@
|
|||||||
|
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import { rankItem } from "@tanstack/match-sorter-utils";
|
||||||
import {
|
import {
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
getPaginationRowModel,
|
getPaginationRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import RichTableBody from "./RichTableBody";
|
import RichTableBody from "./RichTableBody";
|
||||||
|
import RichTableColumnsDropdown from "./RichTableColumnsDropdown";
|
||||||
import RichTableHeader from "./RichTableHeader";
|
import RichTableHeader from "./RichTableHeader";
|
||||||
import RichTablePagination from "./RichTablePagination";
|
import RichTablePagination from "./RichTablePagination";
|
||||||
|
import Input from "../../bootstrap/Input";
|
||||||
|
|
||||||
RichTable.propTypes = {
|
RichTable.propTypes = {
|
||||||
/** Columns to be displayed in the table */
|
/** Columns to be displayed in the table */
|
||||||
@ -46,36 +50,69 @@ export default function RichTable({
|
|||||||
pageIndex,
|
pageIndex,
|
||||||
pageSize,
|
pageSize,
|
||||||
});
|
});
|
||||||
|
const [globalFilter, setGlobalFilter] = useState("");
|
||||||
|
const [columnVisibility, setColumnVisibility] = useState({});
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns: tableColumns,
|
columns: tableColumns,
|
||||||
|
filterFns: {
|
||||||
|
fuzzy: fuzzyFilter,
|
||||||
|
},
|
||||||
|
globalFilterFn: "fuzzy",
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
onPaginationChange: setPagination,
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
onGlobalFilterChange: setGlobalFilter,
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
state: {
|
state: {
|
||||||
sorting,
|
sorting,
|
||||||
pagination,
|
pagination,
|
||||||
|
globalFilter,
|
||||||
|
columnVisibility,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const paginationIsNeeded = data.length > pageSize && withPagination;
|
const paginationIsNeeded = data.length > pageSize && withPagination;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="table-responsive">
|
<div>
|
||||||
<table className="table table-hover text-nowrap">
|
<div className="d-flex justify-content-between align-items-center">
|
||||||
<RichTableHeader table={table} flexRender={flexRender} />
|
<Input
|
||||||
<RichTableBody table={table} flexRender={flexRender} />
|
className="me-3"
|
||||||
</table>
|
type="text"
|
||||||
{paginationIsNeeded && (
|
placeholder={_("Search…")}
|
||||||
<RichTablePagination
|
value={globalFilter ?? ""}
|
||||||
table={table}
|
onChange={(e) => setGlobalFilter(String(e.target.value))}
|
||||||
tablePageSize={pageSize}
|
|
||||||
allRows={data.length}
|
|
||||||
/>
|
/>
|
||||||
)}
|
<RichTableColumnsDropdown columns={table.getAllLeafColumns()} />
|
||||||
|
</div>
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-hover text-nowrap">
|
||||||
|
<RichTableHeader table={table} flexRender={flexRender} />
|
||||||
|
<RichTableBody
|
||||||
|
table={table}
|
||||||
|
columns={tableColumns}
|
||||||
|
flexRender={flexRender}
|
||||||
|
/>
|
||||||
|
</table>
|
||||||
|
{paginationIsNeeded && (
|
||||||
|
<RichTablePagination
|
||||||
|
table={table}
|
||||||
|
tablePageSize={pageSize}
|
||||||
|
allRows={data.length}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fuzzyFilter(row, columnId, value, addMeta) {
|
||||||
|
const itemRank = rankItem(row.getValue(columnId), value);
|
||||||
|
addMeta({ itemRank });
|
||||||
|
return itemRank.passed;
|
||||||
|
}
|
||||||
|
@ -13,34 +13,44 @@ RichTableBody.propTypes = {
|
|||||||
table: propTypes.shape({
|
table: propTypes.shape({
|
||||||
getRowModel: propTypes.func.isRequired,
|
getRowModel: propTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
columns: propTypes.array.isRequired,
|
||||||
flexRender: propTypes.func.isRequired,
|
flexRender: propTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
function RichTableBody({ table, flexRender }) {
|
function RichTableBody({ table, columns, flexRender }) {
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
{table.getRowModel().rows.map((row) => {
|
{table.getRowModel().rows?.length ? (
|
||||||
return (
|
table.getRowModel().rows.map((row) => {
|
||||||
<tr key={row.id} className="align-middle">
|
return (
|
||||||
{row.getVisibleCells().map((cell) => {
|
<tr key={row.id} className="align-middle">
|
||||||
return (
|
{row.getVisibleCells().map((cell) => {
|
||||||
<td
|
return (
|
||||||
key={cell.id}
|
<td
|
||||||
{...(cell.column.columnDef.className && {
|
key={cell.id}
|
||||||
className:
|
{...(cell.column.columnDef
|
||||||
cell.column.columnDef.className,
|
.className && {
|
||||||
})}
|
className:
|
||||||
>
|
cell.column.columnDef.className,
|
||||||
{flexRender(
|
})}
|
||||||
cell.column.columnDef.cell,
|
>
|
||||||
cell.getContext()
|
{flexRender(
|
||||||
)}
|
cell.column.columnDef.cell,
|
||||||
</td>
|
cell.getContext()
|
||||||
);
|
)}
|
||||||
})}
|
</td>
|
||||||
</tr>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</tr>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={columns.length} className="text-center py-4">
|
||||||
|
<span>{_("No results.")}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
90
src/common/RichTable/RichTableColumnsDropdown.js
Normal file
90
src/common/RichTable/RichTableColumnsDropdown.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2025 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
||||||
|
*
|
||||||
|
* This is free software, licensed under the GNU General Public License v3.
|
||||||
|
* See /LICENSE for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { faCheck, faRotateLeft } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
import Button from "../../bootstrap/Button";
|
||||||
|
|
||||||
|
RichTableColumnsDropdown.propTypes = {
|
||||||
|
columns: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function RichTableColumnsDropdown({ columns }) {
|
||||||
|
return (
|
||||||
|
<div className="dropdown mb-3">
|
||||||
|
<Button
|
||||||
|
className="btn btn-outline-secondary dropdown-toggle"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
>
|
||||||
|
{_("Columns")}
|
||||||
|
</Button>
|
||||||
|
<ul className="dropdown-menu dropdown-menu-end">
|
||||||
|
{columns.map((column) => {
|
||||||
|
return (
|
||||||
|
<li key={column.id}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="dropdown-item d-flex align-items-center"
|
||||||
|
onClick={column.getToggleVisibilityHandler()}
|
||||||
|
style={{ paddingLeft: "2rem" }}
|
||||||
|
disabled={
|
||||||
|
column.columnDef?.enableHiding === false
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{column.getIsVisible() && (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCheck}
|
||||||
|
className="position-absolute text-secondary me-2"
|
||||||
|
style={{ left: "0.6rem" }}
|
||||||
|
width="1rem"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>{column.columnDef.header}</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{columns.some((column) => !column.getIsVisible()) && (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
<hr className="dropdown-divider" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="dropdown-item d-flex align-items-center"
|
||||||
|
style={{ paddingLeft: "2rem" }}
|
||||||
|
onClick={() => {
|
||||||
|
// toggleVisibility for columns that are hidden
|
||||||
|
columns.forEach((column) => {
|
||||||
|
if (!column.getIsVisible()) {
|
||||||
|
column.toggleVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faRotateLeft}
|
||||||
|
className="position-absolute text-secondary me-2"
|
||||||
|
width="1rem"
|
||||||
|
style={{ left: "0.6rem" }}
|
||||||
|
/>
|
||||||
|
{_("Reset")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RichTableColumnsDropdown;
|
@ -55,6 +55,12 @@ function RichTableHeader({ table, flexRender }) {
|
|||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
style={
|
||||||
|
header.column.columnDef
|
||||||
|
.headerClassName === "text-center"
|
||||||
|
? { justifySelf: "center" }
|
||||||
|
: {}
|
||||||
|
}
|
||||||
className={`btn btn-link text-decoration-none text-reset fw-bold p-0 d-flex align-items-center
|
className={`btn btn-link text-decoration-none text-reset fw-bold p-0 d-flex align-items-center
|
||||||
${
|
${
|
||||||
header.column.getCanSort()
|
header.column.getCanSort()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
* Copyright (C) 2019-2025 CZ.NIC z.s.p.o. (https://www.nic.cz/)
|
||||||
*
|
*
|
||||||
* This is free software, licensed under the GNU General Public License v3.
|
* This is free software, licensed under the GNU General Public License v3.
|
||||||
* See /LICENSE for more information.
|
* See /LICENSE for more information.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user