import React, {useEffect, useRef, useCallback} from 'react'; import { useTable, useExpanded, useRowSelect, usePagination, useResizeColumns, useSortBy, useFlexLayout } from 'react-table'; import { Checkbox, Spinner } from '@blueprintjs/core'; import classnames from 'classnames'; import { FixedSizeList } from 'react-window' import { ConditionalWrapper } from 'utils'; import { useUpdateEffect } from 'hooks'; const IndeterminateCheckbox = React.forwardRef( ({ indeterminate, ...rest }, ref) => { return (); } ); export default function DataTable({ columns, data, loading, onFetchData, onSelectedRowsChange, manualSortBy = 'false', selectionColumn = false, expandSubRows = true, className, noResults = 'This report does not contain any data.', expanded = {}, rowClassNames, stickyHeader = true, virtualizedRows = false, fixedSizeHeight = 100, fixedItemSize = 30, payload, expandable = false, expandToggleColumn = 2, noInitialFetch = false, spinnerProps = { size: 40 }, }) { const { getTableProps, getTableBodyProps, headerGroups, prepareRow, page, rows, canPreviousPage, canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, setPageSize, selectedFlatRows, totalColumnsWidth, getToggleAllRowsExpandedProps, isAllRowsExpanded, // Get the state from the instance state: { pageIndex, pageSize, sortBy, selectedRowIds, selectedRows }, } = useTable( { columns, data: data, initialState: { pageIndex: 0, expanded }, // Pass our hoisted table state manualPagination: true, // Tell the usePagination // hook that we'll handle our own data fetching // This means we'll also have to provide our own // pageCount. // pageCount: controlledPageCount, getSubRows: row => row.children, manualSortBy, expandSubRows, payload, autoResetSelectedRows: false, }, useSortBy, useExpanded, useRowSelect, usePagination, useResizeColumns, useFlexLayout, hooks => { hooks.visibleColumns.push(columns => [ // Let's make a column for selection ...(selectionColumn) ? [{ id: 'selection', disableResizing: true, minWidth: 42, width: 42, maxWidth: 42, // The header can use the table's getToggleAllRowsSelectedProps method // to render a checkbox Header: ({ getToggleAllRowsSelectedProps }) => (
), // The cell can use the individual row's getToggleRowSelectedProps method // to the render a checkbox Cell: ({ row }) => (
), className: 'selection', ...(typeof selectionColumn === 'object') ? selectionColumn : {}, }] : [], ...columns, ]) } ); const isInitialMount = useRef(noInitialFetch); // When these table states change, fetch new data! useEffect(() => { if (isInitialMount.current) { isInitialMount.current = false; } else { onFetchData && onFetchData({ pageIndex, pageSize, sortBy }) } }, [pageIndex, pageSize, sortBy]); useUpdateEffect(() => { onSelectedRowsChange && onSelectedRowsChange(selectedFlatRows); }, [selectedRowIds, onSelectedRowsChange]); // Renders table cell. const RenderCell = useCallback(({ row, cell, index }) => ( (
{children}
)} > { // Use the row.canExpand and row.getToggleRowExpandedProps prop getter // to build the toggle for expanding a row (row.canExpand && expandable && index === expandToggleColumn) && ( ) } { cell.render('Cell') }
), [expandable, expandToggleColumn]); // Renders table row. const RenderRow = useCallback(({ style = {}, row }) => { prepareRow(row); const rowClasses = rowClassNames && rowClassNames(row); return (
{row.cells.map((cell, i) => { const index = i + 1; return
{ RenderCell({ cell, row, index }) }
})}
); }, [prepareRow, rowClassNames, expandable, RenderCell, expandToggleColumn]); // Renders virtualize circle table rows. const RenderVirtualizedRows = useCallback(({ index, style }) => { const row = rows[index]; return RenderRow({ row, style }); }, [RenderRow, rows]); const RenderPage = useCallback(({ style, index } = {}) => { return page.map((row, index) => RenderRow({ row })); }, [RenderRow, page]); // Renders fixed size tbody. const RenderTBody = useCallback(() => { return (virtualizedRows) ? ( {RenderVirtualizedRows} ) : RenderPage(); }, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows, RenderVirtualizedRows, RenderPage]); return (
{headerGroups.map(headerGroup => (
{headerGroup.headers.map((column, index) => (
{(expandable && (index + 1) === expandToggleColumn) && ( )}
{column.render('Header')} {column.isSorted && ( )}
{column.canResize && (
)}
))}
))}
{ !loading && RenderTBody() } { !loading && (page.length === 0) && (
{ noResults }
)} { loading && (
) }
) }