1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2024-12-26 00:21:36 +01:00

fixup! Add RichTable component with header, body, and pagination

This commit is contained in:
Aleksandr Gumroian 2024-11-05 22:37:17 +01:00
parent fe183e0b70
commit 6d0787dc72
No known key found for this signature in database
GPG Key ID: 9E77849C64F0A733
4 changed files with 103 additions and 138 deletions

View File

@ -37,8 +37,8 @@ const RichTable = ({
}); });
const table = useReactTable({ const table = useReactTable({
columns: tableColumns,
data: tableData, data: tableData,
columns: tableColumns,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
@ -57,7 +57,11 @@ const RichTable = ({
<RichTableBody table={table} flexRender={flexRender} /> <RichTableBody table={table} flexRender={flexRender} />
</table> </table>
{withPagination && ( {withPagination && (
<RichTablePagination table={table} tablePageSize={pageSize} /> <RichTablePagination
table={table}
tablePageSize={pageSize}
allRows={tableData.length}
/>
)} )}
</div> </div>
); );

View File

@ -12,10 +12,16 @@ const RichTableBody = ({ table, flexRender }) => {
<tbody> <tbody>
{table.getRowModel().rows.map((row) => { {table.getRowModel().rows.map((row) => {
return ( return (
<tr key={row.id}> <tr key={row.id} className="align-middle">
{row.getVisibleCells().map((cell) => { {row.getVisibleCells().map((cell) => {
return ( return (
<td key={cell.id}> <td
key={cell.id}
{...(cell.column.columnDef.className && {
className:
cell.column.columnDef.className,
})}
>
{flexRender( {flexRender(
cell.column.columnDef.cell, cell.column.columnDef.cell,
cell.getContext() cell.getContext()

View File

@ -26,44 +26,43 @@ const RichTableHeader = ({ table, flexRender }) => {
<thead className="thead-light"> <thead className="thead-light">
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}> <tr key={headerGroup.id}>
{headerGroup.headers.map((header) => { {headerGroup.headers.map((header) => (
return ( <th key={header.id} colSpan={header.colSpan}>
<th key={header.id} colSpan={header.colSpan}> {header.isPlaceholder ||
{header.isPlaceholder ? null : ( !header.column.columnDef.header ? null : (
<button <button
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()
? "d-flex align-items-center" ? "d-flex align-items-center"
: "" : ""
} }
`} `}
onClick={header.column.getToggleSortingHandler()} onClick={header.column.getToggleSortingHandler()}
title={getThTitle(header)} title={getThTitle(header)}
> >
{flexRender( {flexRender(
header.column.columnDef.header, header.column.columnDef.header,
header.getContext() header.getContext()
)} )}
{{ {{
asc: ( asc: (
<FontAwesomeIcon <FontAwesomeIcon
icon={faSquareCaretUp} icon={faSquareCaretUp}
className="ms-1 text-primary" className="ms-1 text-primary"
/> />
), ),
desc: ( desc: (
<FontAwesomeIcon <FontAwesomeIcon
icon={faSquareCaretDown} icon={faSquareCaretDown}
className="ms-1 text-primary" className="ms-1 text-primary"
/> />
), ),
}[header.column.getIsSorted()] ?? null} }[header.column.getIsSorted()] ?? null}
</button> </button>
)} )}
</th> </th>
); ))}
})}
</tr> </tr>
))} ))}
</thead> </thead>

View File

@ -5,7 +5,7 @@
* See /LICENSE for more information. * See /LICENSE for more information.
*/ */
import React from "react"; import React, { useMemo } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faAngleLeft, faAngleLeft,
@ -14,89 +14,68 @@ import {
faAnglesRight, faAnglesRight,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
const RichTablePagination = ({ table, tablePageSize }) => { const RichTablePagination = ({ table, tablePageSize, allRows }) => {
const { pagination } = table.getState();
const prevPagBtnDisabled = !table.getCanPreviousPage(); const prevPagBtnDisabled = !table.getCanPreviousPage();
const nextPagBtnDisabled = !table.getCanNextPage(); 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) => (
<li
className={`page-item ${disabled ? "disabled" : ""}`}
style={{ cursor: disabled ? "not-allowed" : "pointer" }}
>
<button
className="page-link"
aria-label={ariaLabel}
onClick={onClick}
disabled={disabled}
>
<FontAwesomeIcon icon={icon} />
</button>
</li>
);
return ( return (
<nav <nav
aria-label={_("Pagination navigation bar")} aria-label={_("Pagination navigation bar")}
className="d-flex gap-2 justify-content-start align-items-center mx-2 mb-1 text-nowrap" className="d-flex gap-2 justify-content-start align-items-center mx-2 mb-1 text-nowrap"
> >
<ul className="pagination pagination-sm mb-0"> <ul className="pagination pagination-sm mb-0">
<li {renderPaginationButton(
className={`page-item ${prevPagBtnDisabled ? "disabled" : ""}`.trim()} faAnglesLeft,
style={ _("First page"),
prevPagBtnDisabled () => table.firstPage(),
? { cursor: "not-allowed" } prevPagBtnDisabled
: { cursor: "pointer" } )}
} {renderPaginationButton(
> faAngleLeft,
<button _("Previous page"),
className="page-link" () => table.previousPage(),
aria-label={_("First page")} prevPagBtnDisabled
onClick={() => table.firstPage()} )}
disabled={prevPagBtnDisabled} {renderPaginationButton(
> faAngleRight,
<FontAwesomeIcon icon={faAnglesLeft} /> _("Next page"),
</button> () => table.nextPage(),
</li> nextPagBtnDisabled
<li )}
className={`page-item ${prevPagBtnDisabled ? "disabled" : ""}`.trim()} {renderPaginationButton(
style={ faAnglesRight,
prevPagBtnDisabled _("Last page"),
? { cursor: "not-allowed" } () => table.lastPage(),
: { cursor: "pointer" } nextPagBtnDisabled
} )}
>
<button
className="page-link"
aria-label={_("Previous page")}
onClick={() => table.previousPage()}
disabled={prevPagBtnDisabled}
>
<FontAwesomeIcon icon={faAngleLeft} />
</button>
</li>
<li
className={`page-item ${nextPagBtnDisabled ? "disabled" : ""}`.trim()}
style={
nextPagBtnDisabled
? { cursor: "not-allowed" }
: { cursor: "pointer" }
}
>
<button
className="page-link"
aria-label={_("Next page")}
onClick={() => table.nextPage()}
disabled={nextPagBtnDisabled}
>
<FontAwesomeIcon icon={faAngleRight} />
</button>
</li>
<li
className={`page-item ${nextPagBtnDisabled ? "disabled" : ""}`.trim()}
style={
nextPagBtnDisabled
? { cursor: "not-allowed" }
: { cursor: "pointer" }
}
>
<button
className="page-link"
aria-label={_("Last page")}
onClick={() => table.lastPage()}
disabled={nextPagBtnDisabled}
>
<FontAwesomeIcon icon={faAnglesRight} />
</button>
</li>
</ul> </ul>
<span> <span>
{_("Page")}&nbsp; {_("Page")}&nbsp;
<span className="fw-bold"> <span className="fw-bold">
{table.getState().pagination.pageIndex + 1} {pagination.pageIndex + 1}
&nbsp;{_("of")}&nbsp; &nbsp;{_("of")}&nbsp;
{table.getPageCount().toLocaleString()} {table.getPageCount().toLocaleString()}
</span> </span>
@ -105,46 +84,23 @@ const RichTablePagination = ({ table, tablePageSize }) => {
className="vr mx-1 align-self-center" className="vr mx-1 align-self-center"
style={{ height: "1.5rem" }} style={{ height: "1.5rem" }}
/> />
<span> <span>{_("Rows per page:")}</span>
{_("Go to page:")}
<div className="d-inline-block ms-1 input-group input-group-sm w-auto">
<input
type="number"
min="1"
max={table.getPageCount()}
defaultValue={table.getState().pagination.pageIndex + 1}
onChange={(e) => {
const page = e.target.value
? Number(e.target.value) - 1
: 0;
table.setPageIndex(page);
}}
className="form-control w-auto"
aria-label={_("Page number")}
/>
</div>
</span>
<select <select
className="form-select form-select-sm w-auto" className="form-select form-select-sm w-auto"
aria-label={_("Select page size")} aria-label={_("Select rows per page")}
value={table.getState().pagination.pageSize} value={pagination.pageSize}
onChange={(e) => { onChange={(e) => {
table.setPageSize(Number(e.target.value)); table.setPageSize(Number(e.target.value));
}} }}
> >
{[ {pageSizes.map((pageSize) => (
// if tablePageSize is not in the list, add it
tablePageSize === 10 ? null : tablePageSize,
10,
20,
30,
40,
50,
].map((pageSize) => (
<option key={pageSize} value={pageSize}> <option key={pageSize} value={pageSize}>
{_("Show")} {pageSize} {pageSize}
</option> </option>
))} ))}
<option key={allRows} value={allRows}>
{_("All")}
</option>
</select> </select>
</nav> </nav>
); );