/* eslint-disable react/jsx-props-no-spreading */ import React, { useState, ReactNode, CSSProperties, RefObject } from 'react'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons/faChevronLeft'; import { faChevronRight } from '@fortawesome/free-solid-svg-icons/faChevronRight'; import clsx from 'clsx'; // eslint-disable-next-line css-modules/no-unused-class import styles from './Table.module.scss'; import LoadingSpinner from './LoadingSpinner'; import Button from './Button'; interface CustomProp { [k: string]: string | CSSProperties | ReactNode | number | undefined; } export interface Cell extends CustomProp { value: ReactNode | string; style?: CSSProperties; } interface HeadCell extends CustomProp { name: string; label: string; sortable?: number; default?: boolean; } export interface BodyRow { 'data-row'?: string; isRowSelected?: boolean; isRowDisabled?: boolean; cells: Cell[]; onClick?: () => void; className?: string; } export type TableBodyType = | { type: 'not-filled'; value: string | ReactNode; bodyClassName?: string; } | { type: 'filled'; bodyRows: BodyRow[]; }; type Table = TableBodyType & { headRow: HeadCell[]; }; interface TableSortProps { sortBy: string; updateSortParams: (v: string) => void; sortByDirection: 'desc' | 'asc'; } export const useTableSort = (headRow: HeadCell[]): TableSortProps => { const defaultSortByCell = headRow.filter((row) => row?.default)[0] || headRow[0]; const [sortBy, updateSortBy] = useState(defaultSortByCell.name); const [sortByDirection, setSortByDirection] = useState<'desc' | 'asc'>( 'desc' ); const updateSortParams = (newSortBy: string) => { let dir = sortByDirection; if (sortBy === newSortBy) { dir = dir === 'asc' ? 'desc' : 'asc'; } else { dir = 'desc'; } updateSortBy(newSortBy); setSortByDirection(dir); }; return { sortBy, sortByDirection, updateSortParams }; }; interface TableProps { sortByDirection?: string; sortBy?: string; updateSortParams?: (newSortBy: string) => void; table: Table; tableBodyRef?: RefObject; className?: string; isLoading?: boolean; /* enables pagination */ itemsPerPage?: number; } function Table({ sortByDirection, sortBy, updateSortParams, table, tableBodyRef, className, isLoading, itemsPerPage, }: TableProps) { const hasSort = sortByDirection && sortBy && updateSortParams; const [currPage, setCurrPage] = useState(0); return isLoading ? (
) : ( <> {table.headRow.map( ({ sortable, label, name, ...rest }, idx: number) => !sortable || table.type === 'not-filled' || !hasSort ? ( // eslint-disable-next-line react/no-array-index-key ) : ( ) )} {table.type === 'not-filled' ? ( ) : ( paginate(table.bodyRows, currPage, itemsPerPage).map( ({ cells, isRowSelected, isRowDisabled, className, ...rest }) => { // The problem is that when you switch apps or time-range and the function // names stay the same it leads to an issue where rows don't get re-rendered // So we force a rerender each time. const renderID = Math.random(); return ( {cells && cells.map( ({ style, value, ...rest }: Cell, index: number) => ( // eslint-disable-next-line react/no-array-index-key ) )} ); } ) )}
{label} updateSortParams(name)} > {label}
{table.value}
{value}
); } function paginate( bodyRows: Extract['bodyRows'], currPage: number, itemsPerPage?: TableProps['itemsPerPage'] ) { if (!itemsPerPage) { return bodyRows; } return bodyRows.slice(currPage * itemsPerPage, itemsPerPage * (currPage + 1)); } interface PaginationNavigationProps { bodyRows?: Extract['bodyRows']; currPage: number; itemsPerPage?: TableProps['itemsPerPage']; setCurrPage: (i: number) => void; } function PaginationNavigation({ itemsPerPage, currPage, setCurrPage, bodyRows, }: PaginationNavigationProps) { if (!itemsPerPage) { return null; } const isThereNextPage = bodyRows ? paginate(bodyRows, currPage + 1, itemsPerPage).length > 0 : false; const isTherePreviousPage = bodyRows ? paginate(bodyRows, currPage - 1, itemsPerPage).length > 0 : false; return ( ); } export default Table;