material-react-table
Version:
A fully featured Material UI V6 implementation of TanStack React Table V8, written from the ground up in TypeScript.
214 lines (199 loc) • 6.15 kB
text/typescript
import { type CSSProperties } from 'react';
import { type TableCellProps } from '@mui/material/TableCell';
import { type TooltipProps } from '@mui/material/Tooltip';
import { alpha, darken, lighten } from '@mui/material/styles';
import { type Theme } from '@mui/material/styles';
import {
type MRT_Column,
type MRT_Header,
type MRT_RowData,
type MRT_TableInstance,
type MRT_TableOptions,
type MRT_Theme,
} from '../types';
import { parseFromValuesOrFunc } from './utils';
export const parseCSSVarId = (id: string) => id.replace(/[^a-zA-Z0-9]/g, '_');
export const getMRTTheme = <TData extends MRT_RowData>(
mrtTheme: MRT_TableOptions<TData>['mrtTheme'],
muiTheme: Theme,
): MRT_Theme => {
const mrtThemeOverrides = parseFromValuesOrFunc(mrtTheme, muiTheme);
const baseBackgroundColor =
mrtThemeOverrides?.baseBackgroundColor ??
(muiTheme.palette.mode === 'dark'
? lighten(muiTheme.palette.background.default, 0.05)
: muiTheme.palette.background.default);
return {
baseBackgroundColor,
cellNavigationOutlineColor: muiTheme.palette.primary.main,
draggingBorderColor: muiTheme.palette.primary.main,
matchHighlightColor:
muiTheme.palette.mode === 'dark'
? darken(muiTheme.palette.warning.dark, 0.25)
: lighten(muiTheme.palette.warning.light, 0.5),
menuBackgroundColor: lighten(baseBackgroundColor, 0.07),
pinnedRowBackgroundColor: alpha(muiTheme.palette.primary.main, 0.1),
selectedRowBackgroundColor: alpha(muiTheme.palette.primary.main, 0.2),
...mrtThemeOverrides,
};
};
export const commonCellBeforeAfterStyles = {
content: '""',
height: '100%',
left: 0,
position: 'absolute',
top: 0,
width: '100%',
zIndex: -1,
};
export const getCommonPinnedCellStyles = <TData extends MRT_RowData>({
column,
table,
theme,
}: {
column?: MRT_Column<TData>;
table: MRT_TableInstance<TData>;
theme: Theme;
}) => {
const { baseBackgroundColor } = table.options.mrtTheme;
const isPinned = column?.getIsPinned();
return {
'&[data-pinned="true"]': {
'&:before': {
backgroundColor: alpha(
darken(
baseBackgroundColor,
theme.palette.mode === 'dark' ? 0.05 : 0.01,
),
0.97,
),
boxShadow: column
? isPinned === 'left' && column.getIsLastColumn(isPinned)
? `-4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: isPinned === 'right' && column.getIsFirstColumn(isPinned)
? `4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: undefined
: undefined,
...commonCellBeforeAfterStyles,
},
},
};
};
export const getCommonMRTCellStyles = <TData extends MRT_RowData>({
column,
header,
table,
tableCellProps,
theme,
}: {
column: MRT_Column<TData>;
header?: MRT_Header<TData>;
table: MRT_TableInstance<TData>;
tableCellProps: TableCellProps;
theme: Theme;
}) => {
const {
getState,
options: { enableColumnVirtualization, layoutMode },
} = table;
const { draggingColumn } = getState();
const { columnDef } = column;
const { columnDefType } = columnDef;
const isColumnPinned =
columnDef.columnDefType !== 'group' && column.getIsPinned();
const widthStyles: CSSProperties = {
minWidth: `max(calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size) * 1px), ${columnDef.minSize ?? 30}px)`,
width: `calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size) * 1px)`,
};
if (layoutMode === 'grid') {
widthStyles.flex = `${
[0, false].includes(columnDef.grow!)
? 0
: `var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size)`
} 0 auto`;
} else if (layoutMode === 'grid-no-grow') {
widthStyles.flex = `${+(columnDef.grow || 0)} 0 auto`;
}
const pinnedStyles = isColumnPinned
? {
...getCommonPinnedCellStyles({ column, table, theme }),
left:
isColumnPinned === 'left'
? `${column.getStart('left')}px`
: undefined,
opacity: 0.97,
position: 'sticky',
right:
isColumnPinned === 'right'
? `${column.getAfter('right')}px`
: undefined,
}
: {};
return {
backgroundColor: 'inherit',
backgroundImage: 'inherit',
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
justifyContent:
columnDefType === 'group'
? 'center'
: layoutMode?.startsWith('grid')
? tableCellProps.align
: undefined,
opacity:
table.getState().draggingColumn?.id === column.id ||
table.getState().hoveredColumn?.id === column.id
? 0.5
: 1,
position: 'relative',
transition: enableColumnVirtualization
? 'none'
: `padding 150ms ease-in-out`,
zIndex:
column.getIsResizing() || draggingColumn?.id === column.id
? 2
: columnDefType !== 'group' && isColumnPinned
? 1
: 0,
'&:focus-visible': {
outline: `2px solid ${table.options.mrtTheme.cellNavigationOutlineColor}`,
outlineOffset: '-2px',
},
...pinnedStyles,
...widthStyles,
...(parseFromValuesOrFunc(tableCellProps?.sx, theme) as any),
};
};
export const getCommonToolbarStyles = <TData extends MRT_RowData>({
table,
}: {
table: MRT_TableInstance<TData>;
theme: Theme;
}) => ({
alignItems: 'flex-start',
backgroundColor: table.options.mrtTheme.baseBackgroundColor,
display: 'grid',
flexWrap: 'wrap-reverse',
minHeight: '3.5rem',
overflow: 'hidden',
position: 'relative',
transition: 'all 150ms ease-in-out',
zIndex: 1,
});
export const flipIconStyles = (theme: Theme) =>
theme.direction === 'rtl'
? { style: { transform: 'scaleX(-1)' } }
: undefined;
export const getCommonTooltipProps = (
placement?: TooltipProps['placement'],
): Partial<TooltipProps> => ({
disableInteractive: true,
enterDelay: 1000,
enterNextDelay: 1000,
placement,
});