/* eslint-disable @typescript-eslint/no-unused-vars */ import React, { ReactElement, useMemo, ReactNode } from "react"; import { useTable, usePagination, useSortBy } from "react-table"; import { styled } from "styled-components"; import { ChevronLeft } from "@styled-icons/bootstrap/ChevronLeft/ChevronLeft.esm.js"; import { ChevronRight } from "@styled-icons/bootstrap/ChevronRight/ChevronRight.esm.js"; import DataDownload, { DataDownloadProps } from "../DataDownload.js"; import Toolbar from "../Toolbar.js"; import { TableOptions } from "react-table"; /** * Adapted from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-table/react-table-tests.tsx * For more inspiration see https://codesandbox.io/s/github/ggascoigne/react-table-example?file=/src/Table/Table.tsx:0-62 * See react-table-config.d.ts for full merged types * @types/react-table README has more info on this approach */ declare module "react-table" { /** * Types for Table component. As the library is plugin-based, so are the community types * This module combines the @types/react-table base and plugin types and re-exports them with the same name * This is possible through "interface merging", as long as the type signature stays the same * The Table component that imports 'react-table' will find and use this extended module automatically * Custom properties we support on our Table component are also included. * Adjust this module as feature needs change or if react-library has breaking changes! * Unused plugings are commented out */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface TableOptions extends UseExpandedOptions, // UseFiltersOptions, UseGlobalFiltersOptions, // UseGroupByOptions, UsePaginationOptions, // UseResizeColumnsOptions, // UseRowSelectOptions, // UseRowStateOptions, UseSortByOptions { // Uncomment this if you want to allow any prop to passed to the Table component, but specific is good // Record /** Optional method to pass style. Added to table element */ className?: string; data: D[]; /** Function called for each table header allowing style/className/role props to be overridden */ headerProps?: (header: HeaderGroup) => TableCommonProps; /** Function called for each table column allowing style/className/role props to be overridden */ columnProps?: (column: Column) => TableCommonProps; /** Function called for each table row allowing style/className/role props to be overridden */ rowProps?: (row: Row) => TableCommonProps; /** Function called for each table cell allowing style/className/role props to be overridden */ cellProps?: (cell: Cell) => TableCommonProps; /** Toolbar title */ title?: string | ReactNode; /** Enable toolbar with download option */ downloadEnabled?: boolean; downloadFilename?: DataDownloadProps["filename"]; downloadFormats?: DataDownloadProps["formats"]; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface Hooks extends UseExpandedHooks, UseGroupByHooks, UseRowSelectHooks, UseSortByHooks {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface TableInstance extends UseColumnOrderInstanceProps, UseExpandedInstanceProps, UseFiltersInstanceProps, UseGlobalFiltersInstanceProps, UseGroupByInstanceProps, UsePaginationInstanceProps, UseRowSelectInstanceProps, UseRowStateInstanceProps, UseSortByInstanceProps {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface TableState extends UseColumnOrderState, UseExpandedState, UseFiltersState, UseGlobalFiltersState, UseGroupByState, UsePaginationState, UseResizeColumnsState, UseRowSelectState, UseRowStateState, UseSortByState {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface ColumnInterface extends UseFiltersColumnOptions, UseGlobalFiltersColumnOptions, UseGroupByColumnOptions, UseResizeColumnsColumnOptions, UseSortByColumnOptions, TableCommonProps {} // added style, className, role props // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface ColumnInstance extends UseFiltersColumnProps, UseGroupByColumnProps, UseResizeColumnsColumnProps, UseSortByColumnProps, TableCommonProps {} // added style, className, role props // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface Cell extends UseGroupByCellProps, UseRowStateCellProps {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface Row extends UseExpandedRowProps, UseGroupByRowProps, UseRowSelectRowProps, UseRowStateRowProps {} } export { Column, Row, TableOptions } from "react-table"; // Re-export for user convenience const Button = styled.button` display: inline; color: #777; font-size: 1em; margin: 0px; padding: 0px; background-color: transparent; border: 1px solid #ccc; border-radius: 0.2rem; &:disabled { color: #ccc; } `; export const TableStyled = styled.div` margin: 10px 0px 20px 0px; table { font-family: sans-serif; width: 100%; border-collapse: collapse; th { padding-top: 8px; padding: 5px; font-weight: bolder; } .up-arrow { position: relative; } .up-arrow::before { content: "▲"; position: absolute; left: 2px; bottom: 3px; font-size: 8px; color: #bbb; } .down-arrow { position: relative; } .down-arrow::before { content: "▼"; position: absolute; left: 2px; bottom: 3px; font-size: 8px; color: #bbb; } tr { font-size: 1em; text-align: left; width: 100%; padding: 2px 5px; } td { padding: 3px 5px; } } .pagination { font-family: sans-serif; padding: 0.5rem; color: #999; } .gp-table-toolbar { margin-top: -10px; } .gp-table-toolbar h2 { flex-grow: 1; } `; const defaultPropGetter = () => ({}); /** * Table component suited to geoprocessing client reports. * Builds on the `react-table` useTable hook and re-exports its interface, * so reference those API docs to suit your needs. * @param props * @returns */ export function Table(props: TableOptions): ReactElement { const defaultColumn = useMemo( () => ({ Filter: undefined, // default filter UI Cell: undefined, // default editable cell }), [], ); const { headerProps = defaultPropGetter, columnProps = defaultPropGetter, rowProps = defaultPropGetter, cellProps = defaultPropGetter, title, downloadEnabled, downloadFilename, downloadFormats, data, ...otherProps } = props; // Default table options and state, caller can override and extend this via props const defaultState: Partial> = { disableMultiSort: true, defaultColumn, initialState: { pageSize: 20, // No paging when lower than that }, }; const { getTableProps, getTableBodyProps, headerGroups, prepareRow, page, // Instead of 'rows', page just has rows for the active page rows, canPreviousPage, canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, setPageSize, state: { pageIndex, pageSize, sortBy, groupBy, expanded, filters, selectedRowIds, }, } = useTable( { ...defaultState, ...otherProps, data, }, useSortBy, usePagination, ); return ( {(title || downloadEnabled) && ( {typeof title === "string" ?

{title}

: title} {downloadEnabled && (
)}
)} {headerGroups.map((headerGroup) => { const { key: headerGroupPropKey, ...otherHeaderGroupProps } = headerGroup.getHeaderGroupProps(); return ( {headerGroup.headers.map((column) => { const { key: headerPropKey, ...otherHeaderProps } = column.getHeaderProps([ { className: column.className, style: column.style, }, columnProps(column), // user passed headerProps(column), // user passed ]); return ( ); })} ); })} {page.map((row) => { prepareRow(row); const { key: otherRowPropKey, ...otherRowProps } = row.getRowProps( rowProps(row) || {}, ); return ( {row.cells.map((cell) => { const cellVal = cell.value; const { key: otherCellPropKey, ...otherCellProps } = cell.getCellProps([ { className: cell.column.className, style: cell.column.style, }, columnProps(cell.column), cellProps(cell), ]); return ( ); })} ); })}
{column.canGroupBy ? ( {column.isGrouped ? "🛑 " : "👊 "} ) : null} {column.render("Header")} {column.isSorted ? ( column.isSortedDesc ? ( ) : ( ) ) : ( "" )}
{/* Render the columns filter UI */} {/*
{column.canFilter ? column.render("Filter") : null}
*/}
{cell.isGrouped ? ( // If it's a grouped cell, add an expander and row count <> {row.isExpanded ? "👇" : "👉"} {" "} {cell.render("Cell", { editable: false })} ( {row.subRows.length}) ) : cell.isAggregated ? ( // If the cell is aggregated, use the Aggregated // renderer for cell cell.render("Aggregated") ) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null // Otherwise, just render the regular cell cellVal )}
{" "} {" "} Page {pageIndex + 1} of {pageOptions.length}
); } export default Table;