UNPKG

material-react-table

Version:

A fully featured Material UI V5 implementation of TanStack React Table V8, written from the ground up in TypeScript.

268 lines (252 loc) 8.29 kB
import type { ColumnOrderState, GroupingState } from '@tanstack/react-table'; import { MRT_FilterFns } from './filterFns'; import { MRT_SortingFns } from './sortingFns'; import { alpha, lighten } from '@mui/material/styles'; import type { TableCellProps } from '@mui/material/TableCell'; import type { Theme } from '@mui/material/styles'; import type { MaterialReactTableProps, MRT_Column, MRT_ColumnDef, MRT_DefinedColumnDef, MRT_DisplayColumnIds, MRT_FilterOption, MRT_Header, MRT_TableInstance, } from '.'; export const getColumnId = <TData extends Record<string, any> = {}>( columnDef: MRT_ColumnDef<TData>, ): string => columnDef.id ?? columnDef.accessorKey?.toString?.() ?? columnDef.header; export const getAllLeafColumnDefs = <TData extends Record<string, any> = {}>( columns: MRT_ColumnDef<TData>[], ): MRT_ColumnDef<TData>[] => { const allLeafColumnDefs: MRT_ColumnDef<TData>[] = []; const getLeafColumns = (cols: MRT_ColumnDef<TData>[]) => { cols.forEach((col) => { if (col.columns) { getLeafColumns(col.columns); } else { allLeafColumnDefs.push(col); } }); }; getLeafColumns(columns); return allLeafColumnDefs; }; export const prepareColumns = <TData extends Record<string, any> = {}>({ columnDefs, columnFilterFns, defaultDisplayColumn, filterFns, sortingFns, }: { columnDefs: MRT_ColumnDef<TData>[]; columnFilterFns: { [key: string]: MRT_FilterOption }; defaultDisplayColumn: Partial<MRT_ColumnDef<TData>>; filterFns: typeof MRT_FilterFns & MaterialReactTableProps<TData>['filterFns']; sortingFns: typeof MRT_SortingFns & MaterialReactTableProps<TData>['sortingFns']; }): MRT_DefinedColumnDef<TData>[] => columnDefs.map((columnDef) => { if (!columnDef.id) columnDef.id = getColumnId(columnDef); if (process.env.NODE_ENV !== 'production' && !columnDef.id) { console.error( 'Column definitions must have a valid `accessorKey` or `id` property', ); } if (!columnDef.columnDefType) columnDef.columnDefType = 'data'; if (columnDef.columns?.length) { columnDef.columnDefType = 'group'; columnDef.columns = prepareColumns({ columnDefs: columnDef.columns, columnFilterFns, defaultDisplayColumn, filterFns, sortingFns, }); } else if (columnDef.columnDefType === 'data') { if (Object.keys(filterFns).includes(columnFilterFns[columnDef.id])) { columnDef.filterFn = filterFns[columnFilterFns[columnDef.id]] ?? filterFns.fuzzy; (columnDef as MRT_DefinedColumnDef)._filterFn = columnFilterFns[columnDef.id]; } if (Object.keys(sortingFns).includes(columnDef.sortingFn as string)) { // @ts-ignore columnDef.sortingFn = sortingFns[columnDef.sortingFn]; } } else if (columnDef.columnDefType === 'display') { columnDef = { ...(defaultDisplayColumn as MRT_ColumnDef<TData>), ...columnDef, }; } return columnDef; }) as MRT_DefinedColumnDef<TData>[]; export const reorderColumn = <TData extends Record<string, any> = {}>( draggedColumn: MRT_Column<TData>, targetColumn: MRT_Column<TData>, columnOrder: ColumnOrderState, ): ColumnOrderState => { if (draggedColumn.getCanPin()) { draggedColumn.pin(targetColumn.getIsPinned()); } columnOrder.splice( columnOrder.indexOf(targetColumn.id), 0, columnOrder.splice(columnOrder.indexOf(draggedColumn.id), 1)[0], ); return [...columnOrder]; }; export const showExpandColumn = <TData extends Record<string, any> = {}>( props: MaterialReactTableProps<TData>, grouping?: GroupingState, ) => !!( props.enableExpanding || (props.enableGrouping && (grouping === undefined || grouping?.length)) || props.renderDetailPanel ); export const getLeadingDisplayColumnIds = < TData extends Record<string, any> = {}, >( props: MaterialReactTableProps<TData>, ) => [ (props.enableRowDragging || props.enableRowOrdering) && 'mrt-row-drag', props.positionActionsColumn === 'first' && (props.enableRowActions || (props.enableEditing && ['row', 'modal'].includes(props.editingMode ?? ''))) && 'mrt-row-actions', props.positionExpandColumn === 'first' && showExpandColumn(props) && 'mrt-row-expand', props.enableRowSelection && 'mrt-row-select', props.enableRowNumbers && 'mrt-row-numbers', ].filter(Boolean) as MRT_DisplayColumnIds[]; export const getTrailingDisplayColumnIds = < TData extends Record<string, any> = {}, >( props: MaterialReactTableProps<TData>, ) => [ props.positionActionsColumn === 'last' && (props.enableRowActions || (props.enableEditing && ['row', 'modal'].includes(props.editingMode ?? ''))) && 'mrt-row-actions', props.positionExpandColumn === 'last' && showExpandColumn(props) && 'mrt-row-expand', ]; export const getDefaultColumnOrderIds = < TData extends Record<string, any> = {}, >( props: MaterialReactTableProps<TData>, ) => [ ...getLeadingDisplayColumnIds(props), ...getAllLeafColumnDefs(props.columns).map((columnDef) => getColumnId(columnDef), ), ...getTrailingDisplayColumnIds(props), ].filter(Boolean) as string[]; export const getDefaultColumnFilterFn = < TData extends Record<string, any> = {}, >( columnDef: MRT_ColumnDef<TData>, ): MRT_FilterOption => { if (columnDef.filterVariant === 'multi-select') return 'arrIncludesSome'; if (columnDef.filterVariant === 'range') return 'betweenInclusive'; if ( columnDef.filterVariant === 'select' || columnDef.filterVariant === 'checkbox' ) return 'equals'; return 'fuzzy'; }; export const getIsLastLeftPinnedColumn = ( table: MRT_TableInstance, column: MRT_Column, ) => { return ( column.getIsPinned() === 'left' && table.getLeftLeafHeaders().length - 1 === column.getPinnedIndex() ); }; export const getIsFirstRightPinnedColumn = (column: MRT_Column) => { return column.getIsPinned() === 'right' && column.getPinnedIndex() === 0; }; export const getTotalRight = (table: MRT_TableInstance, column: MRT_Column) => { return ( (table.getRightLeafHeaders().length - 1 - column.getPinnedIndex()) * 160 ); }; export const getCommonCellStyles = ({ column, header, table, tableCellProps, theme, }: { column: MRT_Column; header?: MRT_Header; table: MRT_TableInstance; tableCellProps: TableCellProps; theme: Theme; }) => ({ backgroundColor: column.getIsPinned() && column.columnDef.columnDefType !== 'group' ? alpha(lighten(theme.palette.background.default, 0.04), 0.97) : 'inherit', backgroundImage: 'inherit', boxShadow: getIsLastLeftPinnedColumn(table, column) ? `-4px 0 8px -6px ${alpha(theme.palette.common.black, 0.2)} inset` : getIsFirstRightPinnedColumn(column) ? `4px 0 8px -6px ${alpha(theme.palette.common.black, 0.2)} inset` : undefined, left: column.getIsPinned() === 'left' ? `${column.getStart('left')}px` : undefined, opacity: table.getState().draggingColumn?.id === column.id || table.getState().hoveredColumn?.id === column.id ? 0.5 : 1, position: column.getIsPinned() && column.columnDef.columnDefType !== 'group' ? 'sticky' : undefined, right: column.getIsPinned() === 'right' ? `${getTotalRight(table, column)}px` : undefined, transition: `all ${column.getIsResizing() ? 0 : '150ms'} ease-in-out`, ...(tableCellProps?.sx instanceof Function ? tableCellProps.sx(theme) : (tableCellProps?.sx as any)), maxWidth: `min(${column.getSize()}px, fit-content)`, minWidth: `max(${column.getSize()}px, ${column.columnDef.minSize ?? 30}px)`, width: header?.getSize() ?? column.getSize(), }); export const MRT_DefaultColumn = { minSize: 40, maxSize: 1000, size: 180, }; export const MRT_DefaultDisplayColumn: Partial<MRT_ColumnDef> = { columnDefType: 'display', enableClickToCopy: false, enableColumnActions: false, enableColumnDragging: false, enableColumnFilter: false, enableColumnOrdering: false, enableEditing: false, enableGlobalFilter: false, enableGrouping: false, enableHiding: false, enableResizing: false, enableSorting: false, };