From b65e034b045af948a6b5878db511219d1a2b930c Mon Sep 17 00:00:00 2001 From: Aleksandr Gumroian Date: Mon, 4 Nov 2024 22:27:14 +0100 Subject: [PATCH 1/2] Add @tanstack/react-table v8.20.5 to dependencies --- package-lock.json | 51 +++++++++++++++++++++++++++++++++++++++++++---- package.json | 1 + 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3cb34d6..92e1fe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", + "@tanstack/react-table": "^8.20.5", "axios": "^1.7.2", "immutability-helper": "^3.1.1", "moment": "^2.30.1", @@ -3583,6 +3584,39 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tanstack/react-table": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz", + "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.20.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.6.1.tgz", @@ -15493,7 +15527,6 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -16247,7 +16280,6 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -21102,6 +21134,19 @@ "@sinonjs/commons": "^3.0.0" } }, + "@tanstack/react-table": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz", + "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==", + "requires": { + "@tanstack/table-core": "8.20.5" + } + }, + "@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==" + }, "@testing-library/dom": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.6.1.tgz", @@ -30083,7 +30128,6 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -30648,7 +30692,6 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" diff --git a/package.json b/package.json index 7fa058e..3599f3e 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", + "@tanstack/react-table": "^8.20.5", "axios": "^1.7.2", "immutability-helper": "^3.1.1", "moment": "^2.30.1", From a3417b58b4842645033ac1fed8c41cb7b4bc4b6e Mon Sep 17 00:00:00 2001 From: Aleksandr Gumroian Date: Mon, 4 Nov 2024 22:27:21 +0100 Subject: [PATCH 2/2] Add RichTable component with header, body, and pagination --- src/common/RichTable/RichTable.js | 70 +++++++++++++ src/common/RichTable/RichTableBody.js | 39 +++++++ src/common/RichTable/RichTableHeader.js | 86 +++++++++++++++ src/common/RichTable/RichTablePagination.js | 109 ++++++++++++++++++++ src/index.js | 1 + 5 files changed, 305 insertions(+) create mode 100644 src/common/RichTable/RichTable.js create mode 100644 src/common/RichTable/RichTableBody.js create mode 100644 src/common/RichTable/RichTableHeader.js create mode 100644 src/common/RichTable/RichTablePagination.js diff --git a/src/common/RichTable/RichTable.js b/src/common/RichTable/RichTable.js new file mode 100644 index 0000000..258ef87 --- /dev/null +++ b/src/common/RichTable/RichTable.js @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019-2024 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, { useMemo, useState } from "react"; + +import { + flexRender, + getCoreRowModel, + getSortedRowModel, + getPaginationRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import RichTableBody from "./RichTableBody"; +import RichTableHeader from "./RichTableHeader"; +import RichTablePagination from "./RichTablePagination"; + +const fallbackData = []; + +const RichTable = ({ + columns, + data, + withPagination, + pageSize = 5, + pageIndex = 0, +}) => { + const tableColumns = useMemo(() => columns, []); + const [tableData, _] = useState(data ?? fallbackData); + const [sorting, setSorting] = useState([]); + const [pagination, setPagination] = useState({ + pageIndex, + pageSize, + }); + + const table = useReactTable({ + data: tableData, + columns: tableColumns, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + onSortingChange: setSorting, + state: { + sorting, + pagination, + }, + }); + + return ( +
+ + + +
+ {withPagination && ( + + )} +
+ ); +}; + +export default RichTable; diff --git a/src/common/RichTable/RichTableBody.js b/src/common/RichTable/RichTableBody.js new file mode 100644 index 0000000..97f6f48 --- /dev/null +++ b/src/common/RichTable/RichTableBody.js @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019-2024 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"; + +const RichTableBody = ({ table, flexRender }) => { + return ( + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ); + })} + + ); + })} + + ); +}; + +export default RichTableBody; diff --git a/src/common/RichTable/RichTableHeader.js b/src/common/RichTable/RichTableHeader.js new file mode 100644 index 0000000..feb204e --- /dev/null +++ b/src/common/RichTable/RichTableHeader.js @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019-2024 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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faSquareCaretUp, + faSquareCaretDown, +} from "@fortawesome/free-solid-svg-icons"; + +const RichTableHeader = ({ table, flexRender }) => { + const getThTitle = (header) => + header.column.getCanSort() + ? header.column.getNextSortingOrder() === "asc" + ? _("Sort ascending") + : header.column.getNextSortingOrder() === "desc" + ? _("Sort descending") + : _("Clear sort") + : undefined; + + return ( + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder || + header.column.columnDef.headerIsHidden ? ( + + ) : ( + + )} + + ))} + + ))} + + ); +}; + +export default RichTableHeader; diff --git a/src/common/RichTable/RichTablePagination.js b/src/common/RichTable/RichTablePagination.js new file mode 100644 index 0000000..c0a5ace --- /dev/null +++ b/src/common/RichTable/RichTablePagination.js @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019-2024 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, { useMemo } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faAngleLeft, + faAnglesLeft, + faAngleRight, + faAnglesRight, +} from "@fortawesome/free-solid-svg-icons"; + +const RichTablePagination = ({ table, tablePageSize, allRows }) => { + const { pagination } = table.getState(); + const prevPagBtnDisabled = !table.getCanPreviousPage(); + const nextPagBtnDisabled = !table.getCanNextPage(); + + const pageSizes = useMemo(() => { + return [tablePageSize ?? 5, 10, 25].filter( + (value, index, self) => self.indexOf(value) === index + ); + }, [tablePageSize]); + + const renderPaginationButton = (icon, ariaLabel, onClick, disabled) => ( +
  • + +
  • + ); + + return ( + + ); +}; + +export default RichTablePagination; diff --git a/src/index.js b/src/index.js index 4e7839f..8052fb1 100644 --- a/src/index.js +++ b/src/index.js @@ -43,6 +43,7 @@ export { Modal, ModalBody, ModalFooter, ModalHeader } from "./bootstrap/Modal"; export { default as RebootButton } from "./common/RebootButton"; export { default as WiFiSettings } from "./common/WiFiSettings/WiFiSettings"; export { default as ResetWiFiSettings } from "./common/WiFiSettings/ResetWiFiSettings"; +export { default as RichTable } from "./common/RichTable/RichTable"; // Form export { default as ForisForm } from "./form/components/ForisForm"; export {