Skip to content

Commit

Permalink
MRT Display Columns with better column resizing performance and less …
Browse files Browse the repository at this point in the history
…memoization (#949)

* display columns in their own hooks

* progress, but will have to revert back to hooks again

* refactor display columns away from hooks still

* improve column resizing performance by caching column defs

* Lint and Prettier

---------

Co-authored-by: KevinVandy <kevinvandy@users.noreply.github.com>
  • Loading branch information
KevinVandy and KevinVandy committed Jan 19, 2024
1 parent ec17f00 commit 185bcbd
Show file tree
Hide file tree
Showing 20 changed files with 595 additions and 480 deletions.
1 change: 1 addition & 0 deletions apps/material-react-table-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@mui/x-date-pickers": "^6.19.0",
"@next/mdx": "^14.0.4",
"@tanstack/react-query": "^5.17.15",
"@tanstack/react-table-devtools": "^8.11.6",
"@types/mdx": "^2.0.10",
"dayjs": "^1.11.10",
"export-to-csv": "^1.2.2",
Expand Down
20 changes: 10 additions & 10 deletions apps/material-react-table-docs/pages/docs/api/mrt-hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,7 @@ const table = useMRT_TableInstance({
});
```

This hook also uses the [`useMRT_DisplayColumns`](#usemrt_displaycolumns) and [`useMRT_Effects`](#usemrt_effects) to generate the built-in display columns (row numbers, actions, selection, etc) and house some extra useEffect hooks needed by some MRT features on a table wide level.

### useMRT_DisplayColumns

[Source Code](https://github.com/KevinVandy/material-react-table/blob/v2/packages/material-react-table/src/hooks/useMRT_DisplayColumns.tsx)

This hook is responsible for generating the built-in display columns (row numbers, actions, selection, etc) and adding them to the table instance's columns array, alongside your own defined columns.
This hook also uses the [`useMRT_Effects`](#usemrt_effects)

### useMRT_Effects

Expand Down Expand Up @@ -121,7 +115,10 @@ return rows.map((row) => {
This hook is a wrapper around the `useVirtualizer` hook from TanStack Virtual. It consumes a `table` instance and returns a Column Virtualizer instance that is optimized for MRT table columns, with considerations for other MRT features like column pinning, column resizing, column hiding, and more.

```tsx
import { useMaterialReactTable, useMRT_ColumnVirtualizer } from 'material-react-table';
import {
useMaterialReactTable,
useMRT_ColumnVirtualizer,
} from 'material-react-table';

const table = useMaterialReactTable({
...options,
Expand All @@ -141,7 +138,10 @@ You would only need to use this hook if you are writing a custom headless table
This hook is a wrapper around the `useVirtualizer` hook from TanStack Virtual. It consumes a `table` instance and returns a Row Virtualizer instance that is optimized for MRT table rows, with considerations for other MRT features like row pinning, row dragging, and more.

```tsx
import { useMaterialReactTable, useMRT_RowVirtualizer } from 'material-react-table';
import {
useMaterialReactTable,
useMRT_RowVirtualizer,
} from 'material-react-table';

const table = useMaterialReactTable({
...options,
Expand All @@ -150,4 +150,4 @@ const table = useMaterialReactTable({
const rowVirtualizer = useMRT_RowVirtualizer(table);
```

You would only need to use this hook if you are writing a custom headless table and want the same default row virtualization behavior that MRT provides. If you are using the MRT components, this hook is already used internally by the `MRT_Table` component.
You would only need to use this hook if you are writing a custom headless table and want the same default row virtualization behavior that MRT provides. If you are using the MRT components, this hook is already used internally by the `MRT_Table` component.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
type MRT_RowData,
type MRT_TableInstance,
} from '../../types';
import { parseFromValuesOrFunc } from '../../utils/utils';
import { getMRTTheme } from '../../utils/style.utils';
import { parseFromValuesOrFunc } from '../../utils/utils';

interface Props<TData extends MRT_RowData> extends TableRowProps {
columnVirtualizer?: MRT_ColumnVirtualizer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type MRT_RowData,
type MRT_TableInstance,
} from '../../types';
import { getDefaultColumnOrderIds } from '../../utils/column.utils';
import { getDefaultColumnOrderIds } from '../../utils/displayColumn.utils';
import { getMRTTheme } from '../../utils/style.utils';

interface Props<TData extends MRT_RowData> extends Partial<MenuProps> {
Expand Down Expand Up @@ -114,9 +114,7 @@ export const MRT_ShowHideColumnsMenu = <TData extends MRT_RowData>({
{enableColumnOrdering && (
<Button
onClick={() =>
table.setColumnOrder(
getDefaultColumnOrderIds(table.options as any),
)
table.setColumnOrder(getDefaultColumnOrderIds(table.options))
}
>
{localization.resetOrder}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import { getMRT_RowActionsColumnDef } from './getMRT_RowActionsColumnDef';
import { getMRT_RowDragColumnDef } from './getMRT_RowDragColumnDef';
import { getMRT_RowExpandColumnDef } from './getMRT_RowExpandColumnDef';
import { getMRT_RowNumbersColumnDef } from './getMRT_RowNumbersColumnDef';
import { getMRT_RowPinningColumnDef } from './getMRT_RowPinningColumnDef';
import { getMRT_RowSelectColumnDef } from './getMRT_RowSelectColumnDef';
import { getMRT_RowSpacerColumnDef } from './getMRT_RowSpacerColumnDef';

export const getMRT_DisplayColumns = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData>[] => {
return [
getMRT_RowNumbersColumnDef(tableOptions),
getMRT_RowSelectColumnDef(tableOptions),
getMRT_RowExpandColumnDef(tableOptions),
getMRT_RowActionsColumnDef(tableOptions),
getMRT_RowDragColumnDef(tableOptions),
getMRT_RowPinningColumnDef(tableOptions),
getMRT_RowSpacerColumnDef(tableOptions),
].filter(Boolean) as MRT_ColumnDef<TData>[];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { MRT_ToggleRowActionMenuButton } from '../../components/buttons/MRT_ToggleRowActionMenuButton';
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowActionsColumn,
} from '../../utils/displayColumn.utils';

export const getMRT_RowActionsColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowActionsColumn(tableOptions)) {
return null;
}

return {
Cell: ({ cell, row, staticRowIndex, table }) => (
<MRT_ToggleRowActionMenuButton
cell={cell}
row={row}
staticRowIndex={staticRowIndex}
table={table}
/>
),
...defaultDisplayColumnProps({
header: 'actions',
id: 'mrt-row-actions',
tableOptions,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { type RefObject } from 'react';
import { MRT_TableBodyRowGrabHandle } from '../../components/body/MRT_TableBodyRowGrabHandle';
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowDragColumn,
} from '../../utils/displayColumn.utils';

export const getMRT_RowDragColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowDragColumn(tableOptions)) {
return null;
}

return {
Cell: ({ row, rowRef, table }) => (
<MRT_TableBodyRowGrabHandle
row={row}
rowRef={rowRef as RefObject<HTMLTableRowElement>}
table={table}
/>
),
...defaultDisplayColumnProps({
header: 'move',
id: 'mrt-row-drag',
tableOptions,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { type ReactNode } from 'react';
import Stack from '@mui/material/Stack';
import Tooltip from '@mui/material/Tooltip';
import { MRT_ExpandAllButton } from '../../components/buttons/MRT_ExpandAllButton';
import { MRT_ExpandButton } from '../../components/buttons/MRT_ExpandButton';
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowExpandColumn,
} from '../../utils/displayColumn.utils';
import { getCommonTooltipProps } from '../../utils/style.utils';

export const getMRT_RowExpandColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowExpandColumn(tableOptions)) {
return null;
}

const {
defaultColumn,
enableExpandAll,
groupedColumnMode,
positionExpandColumn,
state: { grouping },
} = tableOptions;

const alignProps =
positionExpandColumn === 'last'
? ({
align: 'right',
} as const)
: undefined;

return {
Cell: ({ cell, column, row, staticRowIndex, table }) => {
const expandButtonProps = { row, staticRowIndex, table };
const subRowsLength = row.subRows?.length;
if (groupedColumnMode === 'remove' && row.groupingColumnId) {
return (
<Stack alignItems="center" flexDirection="row" gap="0.25rem">
<MRT_ExpandButton {...expandButtonProps} />
<Tooltip
{...getCommonTooltipProps('right')}
title={table.getColumn(row.groupingColumnId).columnDef.header}
>
<span>{row.groupingValue as ReactNode}</span>
</Tooltip>
{!!subRowsLength && <span>({subRowsLength})</span>}
</Stack>
);
} else {
return (
<>
<MRT_ExpandButton {...expandButtonProps} />
{column.columnDef.GroupedCell?.({ cell, column, row, table })}
</>
);
}
},
Header: enableExpandAll
? ({ table }) => {
return (
<>
<MRT_ExpandAllButton table={table} />
{groupedColumnMode === 'remove' &&
grouping
?.map(
(groupedColumnId) =>
table.getColumn(groupedColumnId).columnDef.header,
)
?.join(', ')}
</>
);
}
: undefined,
muiTableBodyCellProps: alignProps,
muiTableHeadCellProps: alignProps,
...defaultDisplayColumnProps({
header: 'expand',
id: 'mrt-row-expand',
size: groupedColumnMode === 'remove' ? defaultColumn?.size : 60,
tableOptions,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowNumbersColumn,
} from '../../utils/displayColumn.utils';

export const getMRT_RowNumbersColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowNumbersColumn(tableOptions)) {
return null;
}

const { rowNumberDisplayMode } = tableOptions;
const {
pagination: { pageIndex, pageSize },
} = tableOptions.state;

return {
Cell: ({ row, staticRowIndex }) =>
((rowNumberDisplayMode === 'static'
? (staticRowIndex || 0) + (pageSize || 0) * (pageIndex || 0)
: row.index) ?? 0) + 1,
...defaultDisplayColumnProps({
header: 'rowNumbers',
id: 'mrt-row-numbers',
tableOptions,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { MRT_TableBodyRowPinButton } from '../../components/body/MRT_TableBodyRowPinButton';
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowPinningColumn,
} from '../../utils/displayColumn.utils';

export const getMRT_RowPinningColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowPinningColumn(tableOptions)) {
return null;
}

return {
Cell: ({ row, table }) => (
<MRT_TableBodyRowPinButton row={row} table={table} />
),
...defaultDisplayColumnProps({
header: 'pin',
id: 'mrt-row-pin',
tableOptions,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MRT_SelectCheckbox } from '../../components/inputs/MRT_SelectCheckbox';
import {
type MRT_ColumnDef,
type MRT_RowData,
type MRT_StatefulTableOptions,
} from '../../types';
import {
defaultDisplayColumnProps,
showRowSelectionColumn,
} from '../../utils/displayColumn.utils';

export const getMRT_RowSelectColumnDef = <TData extends MRT_RowData>(
tableOptions: MRT_StatefulTableOptions<TData>,
): MRT_ColumnDef<TData> | null => {
if (!showRowSelectionColumn(tableOptions)) {
return null;
}

const { enableMultiRowSelection, enableSelectAll } = tableOptions;

return {
Cell: ({ row, staticRowIndex, table }) => (
<MRT_SelectCheckbox
row={row}
staticRowIndex={staticRowIndex}
table={table}
/>
),
Header:
enableSelectAll && enableMultiRowSelection
? ({ table }) => <MRT_SelectCheckbox selectAll table={table} />
: undefined,
...defaultDisplayColumnProps({
header: 'select',
id: 'mrt-row-select',
tableOptions,
}),
};
};
Loading

1 comment on commit 185bcbd

@vercel
Copy link

@vercel vercel bot commented on 185bcbd Jan 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.