/// import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { Column as TableColumn, CellProps as TableCellProps, CoreUIColumn, HeaderGroup, IdType, Row, SortByFn, TableBodyPropGetter, TableBodyProps, useBlockLayout, useExpanded, useFilters, useGlobalFilter, useRowSelect, useSortBy, useTable, defaultColumn, } from 'react-table'; import { useMemoCompare } from '../../hooks'; import { Box } from '../box/Box'; import { ConstrainedText } from '../constrainedtext/Constrainedtext.component'; import { Tooltip } from '../tooltip/Tooltip.component'; import { MultiSelectableContent } from './MultiSelectableContent'; import { TableSearch as Search } from './Search'; import { SearchWithQueryParams } from './SearchWithQueryParams'; import { SingleSelectableContent } from './SingleSelectableContent'; import { TableWrapper, TooltipContent } from './Tablestyle'; import { compareHealth, TableHeightKeyType } from './TableUtils'; import { useCheckbox } from './useCheckbox'; import { Icon } from '../icon/Icon.component'; import { TableSync } from './TableSync'; type UpdateTableData< DATA_ROW extends Record = Record, > = { updateTableData?: ( rowId: string, columnName: DATA_ROW_KEY, value: DATA_ROW[DATA_ROW_KEY], ) => void; }; export type Column> = CoreUIColumn; export type CellProps< D extends Record, V = unknown, > = TableCellProps & UpdateTableData; export type TableProps< DATA_ROW extends Record = Record, > = { columns: Array>; defaultSortingKey?: string; // We don't display the default sort key in the URL, so we need to specify here data: DATA_ROW[]; children: JSX.Element | JSX.Element[]; getRowId?: ( originalRow: DATA_ROW, relativeIndex: number, parent?: Row, ) => string; sortTypes?: Record>; globalFilter?: string; onBottom?: (rowLength: number) => void; onBottomOffset?: number; allFilters?: { id: string; value: string }[]; status?: 'idle' | 'loading' | 'error' | 'success'; entityName?: { en: { singular: string; plural: string }; fr?: { singular: string; plural: string }; }; initiallySelectedRowsIds?: Set; //To call it from the Cell renderer to update the original data } & UpdateTableData; type setHiddenColumnFuncType = (oldHidden: string[]) => string[]; type TableContextType< DATA_ROW extends Record = Record, > = { headerGroups: HeaderGroup[]; rows: Row[]; prepareRow: (row: Row) => void; getTableBodyProps: ( propGetter?: TableBodyPropGetter, ) => TableBodyProps; rowHeight: TableHeightKeyType; setRowHeight: (rowHeight: TableHeightKeyType) => void; selectedRowIds: Record; selectedFlatRows: Row[]; preGlobalFilteredRows: Row[]; setGlobalFilter: (filterValue: string) => void; globalFilter: any; setFilter: (columnId: string, updater: any) => void; onBottom?: (rowLength: number) => void; onBottomOffset?: number; setHiddenColumns: (param: string[] | setHiddenColumnFuncType) => void; isAllRowsSelected?: boolean; toggleAllRowsSelected: (value?: boolean) => void; status?: 'idle' | 'loading' | 'error' | 'success'; entityName?: { en: { singular: string; plural: string }; fr?: { singular: string; plural: string }; }; syncScrollListener: ((event: Event) => void) | null; setSyncScrollListener: (listener: (event: Event) => void) => void; setHasScrollbar: React.Dispatch>; hasScrollbar?: boolean; }; const TableContext = createContext(null); export const useTableContext = < DATA_ROW extends Record = Record, >() => { const tableProps = useContext(TableContext); if (!tableProps) { throw new Error( 'The useTableContext hook can only be used within the ', ); } return tableProps as TableContextType; //Todo figure out a way to transfer the type to the context provider }; export const EmptyCell = ({ tooltipContent, mr = 4, }: { tooltipContent?: string | JSX.Element; mr?: number; }) => { return ( {tooltipContent || 'unknown'}} > ); }; const DefaultRenderer = ({ value }) => { const { rowHeight } = useTableContext(); if (!value && value !== 0) { return ; } if (typeof value === 'string') { const lineClamp = rowHeight === 'h32' ? 1 : rowHeight === 'h40' || rowHeight === 'h48' ? 2 : 3; return ( ); } return value; }; function Table< DATA_ROW extends Record = Record, >({ columns, data, defaultSortingKey, getRowId, children, sortTypes, globalFilter, allFilters, onBottom, onBottomOffset = 10, initiallySelectedRowsIds, updateTableData, status, entityName, }: TableProps) { sortTypes = { health: (row1, row2) => { return compareHealth(row2.values.health, row1.values.health) || 0; }, ...sortTypes, }; const stringifyFilter = useMemo(() => { return (rows: Row[], columnIds: string[], value) => { const searchTerm = (value || '').toLowerCase(); const filteredRows = rows.filter((row) => { return columnIds.some((columnId) => String(row.values[columnId]).toLowerCase().includes(searchTerm), ); }); return filteredRows; }; }, []); const formattedInitiallySelectedRows = useMemo(() => { if (initiallySelectedRowsIds) { return Array.from(initiallySelectedRowsIds).reduce( (accumulatedValue, currentValue) => ({ ...accumulatedValue, [currentValue]: true, }), {}, ); } return {}; }, []) as Record, boolean>; const [rowHeight, setRowHeight] = useState('h40'); const [syncScrollListener, setSyncScrollListener] = useState< ((event: Event) => void) | null >(null); const [hasScrollbar, setHasScrollbar] = useState(false); const { headerGroups, rows, prepareRow, selectedFlatRows, getTableBodyProps, state: { selectedRowIds }, preGlobalFilteredRows, setGlobalFilter, setFilter, setAllFilters, setHiddenColumns, isAllRowsSelected, toggleAllRowsSelected, } = useTable( { defaultColumn: { ...(defaultColumn as TableColumn), Cell: DefaultRenderer, }, columns: columns as TableColumn[], data, getRowId, initialState: { sortBy: defaultSortingKey ? [ { id: defaultSortingKey, desc: false, }, ] : [], selectedRowIds: formattedInitiallySelectedRows, hiddenColumns: ['selection'], }, disableMultiSort: true, autoResetSortBy: false, sortTypes, //@ts-ignore TODO investigate why this type is not matching globalFilter: stringifyFilter, autoResetHiddenColumns: false, updateTableData, }, useBlockLayout, useFilters, useGlobalFilter, useSortBy, useExpanded, useRowSelect, useCheckbox, ); useEffect(() => { if (globalFilter !== undefined && globalFilter !== null) { setGlobalFilter(globalFilter); } }, [globalFilter, setGlobalFilter, data]); const filters = useMemoCompare( allFilters, (previous, next) => !previous || JSON.stringify(previous) === JSON.stringify(next), ); useEffect(() => { if (!filters) { setAllFilters([]); } else { const validFilters = filters.filter( (filter) => filter.id && filter.value !== undefined, ); setAllFilters(validFilters.length > 0 ? validFilters : []); } }, [filters, setAllFilters]); const contextValue: TableContextType = { headerGroups, rows, prepareRow, getTableBodyProps, selectedRowIds, selectedFlatRows, preGlobalFilteredRows, setGlobalFilter, rowHeight, setRowHeight, globalFilter, setFilter, onBottom, onBottomOffset, setHiddenColumns, isAllRowsSelected, toggleAllRowsSelected, status, entityName, syncScrollListener, setSyncScrollListener, setHasScrollbar, hasScrollbar, }; return ( {children} ); } Table.SingleSelectableContent = SingleSelectableContent; Table.MultiSelectableContent = MultiSelectableContent; Table.Search = Search; Table.SearchWithQueryParams = SearchWithQueryParams; Table.Sync = TableSync; export { Table };