From 42294316d9a02b5d5c1b9eb4562881dc55223519 Mon Sep 17 00:00:00 2001 From: Aleksandr Gumroian Date: Mon, 4 Nov 2024 22:27:21 +0100 Subject: [PATCH] Add RichTable component with header, body, and pagination --- src/common/RichTable/RichTable.js | 70 +++++++++++ src/common/RichTable/RichTableBody.js | 48 ++++++++ src/common/RichTable/RichTableHeader.js | 96 +++++++++++++++ src/common/RichTable/RichTablePagination.js | 128 ++++++++++++++++++++ src/index.js | 1 + 5 files changed, 343 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..a45451a --- /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, [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..2aca395 --- /dev/null +++ b/src/common/RichTable/RichTableBody.js @@ -0,0 +1,48 @@ +/* + * 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 propTypes from "prop-types"; + +RichTableBody.propTypes = { + table: propTypes.shape({ + getRowModel: propTypes.func.isRequired, + }).isRequired, + flexRender: propTypes.func.isRequired, +}; + +function 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..79e6ca9 --- /dev/null +++ b/src/common/RichTable/RichTableHeader.js @@ -0,0 +1,96 @@ +/* + * 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 { + faSquareCaretUp, + faSquareCaretDown, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import propTypes from "prop-types"; + +RichTableHeader.propTypes = { + table: propTypes.shape({ + getHeaderGroups: propTypes.func.isRequired, + }).isRequired, + flexRender: propTypes.func.isRequired, +}; + +function RichTableHeader({ table, flexRender }) { + const getThTitle = (header) => { + if (!header.column.getCanSort()) return undefined; + + const nextSortingOrder = header.column.getNextSortingOrder(); + if (nextSortingOrder === "asc") return _("Sort ascending"); + if (nextSortingOrder === "desc") return _("Sort descending"); + return _("Clear sort"); + }; + + 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..c6821f6 --- /dev/null +++ b/src/common/RichTable/RichTablePagination.js @@ -0,0 +1,128 @@ +/* + * 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 { + faAngleLeft, + faAnglesLeft, + faAngleRight, + faAnglesRight, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import propTypes from "prop-types"; + +RichTablePagination.propTypes = { + table: propTypes.shape({ + getState: propTypes.func.isRequired, + getCanPreviousPage: propTypes.func.isRequired, + getCanNextPage: propTypes.func.isRequired, + firstPage: propTypes.func.isRequired, + previousPage: propTypes.func.isRequired, + nextPage: propTypes.func.isRequired, + lastPage: propTypes.func.isRequired, + setPageSize: propTypes.func.isRequired, + getPageCount: propTypes.func.isRequired, + }).isRequired, + tablePageSize: propTypes.number, + allRows: propTypes.number, +}; + +function 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 {