diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16e016699..6e5874220 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,10 +28,16 @@ pnpm i ### 3. Run the project(s) +#### Run the Library for Local Development + +```bash +pnpm lib:dev +``` + #### Run the Storybook for Local Development ```bash -pnpm storybook +pnpm storybook:dev ``` The Storybook site will open on `port 6006` by default. @@ -44,9 +50,9 @@ pnpm docs:dev The Docs site will open on `port 3000` by default. -> Note: If you are contributing a new locale and are trying to test it in the docs site, you will need to run `pnpm lib:build` and then `pnpm docs:dev` before it can be imported. +> Note: If you are contributing a new locale and are trying to test it in the docs site, you will need to run `pnpm lib:build-locales` and then `pnpm docs:dev` before it can be imported. -#### Build the Library +#### Fully Build the Library ```bash pnpm lib:build diff --git a/apps/material-react-table-docs/components/prop-tables/rootProps.ts b/apps/material-react-table-docs/components/prop-tables/rootProps.ts index 27a94462d..a7ce92344 100644 --- a/apps/material-react-table-docs/components/prop-tables/rootProps.ts +++ b/apps/material-react-table-docs/components/prop-tables/rootProps.ts @@ -1743,6 +1743,16 @@ export const rootProps: PropRow[] = [ source: '', type: '({ internalFilterOptions, onSelectFilterMode, table }) => ReactNode[]', }, + { + propName: 'renderEmptyRowsFallback', + defaultValue: '', + description: '', + link: '', + linkText: '', + required: false, + source: '', + type: '({ table }) => ReactNode', + }, { propName: 'renderRowActionMenuItems', defaultValue: '', diff --git a/apps/material-react-table-docs/pages/changelog.mdx b/apps/material-react-table-docs/pages/changelog.mdx index b811a1f33..8d8199548 100644 --- a/apps/material-react-table-docs/pages/changelog.mdx +++ b/apps/material-react-table-docs/pages/changelog.mdx @@ -12,6 +12,14 @@ import Head from 'next/head'; ### Version 1 (Latest) +#### v1.10.0 (2023-04-16) + +- New highly requested `renderEmptyRowsFallback` prop for rendering custom JSX when there are no rows to display +- Added `staticRowIndex` param to `muiTableBodyRowProps` to make styling striped virtual rows easier +- Now allowing table head, body, and footer cell widths to be specified in `sx` prop when `enableColumnRisizing` is disabled. BUT YOU SHOULD STILL USE THE `size` COLUMN OPTION INSTEAD! +- Fixed issue with "Reset Order" button not always being accurate when position props === "last" +- Fixed issue with draggable columns with grouping disabled still being allowed to be dragged to dropzone. + #### v1.9.4 (2023-04-15) - Fixed row actions and row menu actions async memo issues diff --git a/apps/material-react-table-storybook/stories/styling/CustomTableBody.stories.tsx b/apps/material-react-table-storybook/stories/styling/CustomTableBody.stories.tsx index 9b254f28a..7f8998b4e 100644 --- a/apps/material-react-table-storybook/stories/styling/CustomTableBody.stories.tsx +++ b/apps/material-react-table-storybook/stories/styling/CustomTableBody.stories.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Meta } from '@storybook/react'; import MaterialReactTable, { type MRT_ColumnDef } from 'material-react-table'; import { faker } from '@faker-js/faker'; +import { Typography } from '@mui/material'; const meta: Meta = { title: 'Styling/Custom Table Body Examples', @@ -54,3 +55,13 @@ export const CustomTableBody = () => ( }} /> ); + +export const CustomEmptyRowsJSX = () => ( + ( + OMG THERE ARE NO ROWS 😳 + )} + /> +); diff --git a/packages/material-react-table/package.json b/packages/material-react-table/package.json index 0bfa388e1..cb416475d 100644 --- a/packages/material-react-table/package.json +++ b/packages/material-react-table/package.json @@ -1,5 +1,5 @@ { - "version": "1.9.4", + "version": "1.10.0", "license": "MIT", "name": "material-react-table", "description": "A fully featured Material UI V5 implementation of TanStack React Table V8, written from the ground up in TypeScript.", @@ -48,9 +48,11 @@ "build": "pnpm lib:build", "build-locales": "pnpm lib:build-locales", "build-no-locales": "pnpm lib:build-no-locales", + "dev": "pnpm lib:dev", "lib:build": "rm -rf dist && pnpm build-no-locales && pnpm build-locales", "lib:build-locales": "rm -rf locales && rollup -c rollup-locales.config.mjs", "lib:build-no-locales": "rm -rf dist && rollup -c rollup.config.mjs && size-limit", + "lib:dev": "rollup -c rollup.config.mjs --watch", "lint": "eslint .", "size": "size-limit" }, diff --git a/packages/material-react-table/src/MaterialReactTable.tsx b/packages/material-react-table/src/MaterialReactTable.tsx index 8b5f55efe..4d540369c 100644 --- a/packages/material-react-table/src/MaterialReactTable.tsx +++ b/packages/material-react-table/src/MaterialReactTable.tsx @@ -776,6 +776,7 @@ export type MaterialReactTableProps = {}> = | ((props: { isDetailPanel?: boolean; row: MRT_Row; + staticRowIndex: number; table: MRT_TableInstance; }) => TableRowProps); muiTableContainerProps?: @@ -918,6 +919,9 @@ export type MaterialReactTableProps = {}> = onSelectFilterMode: (filterMode: MRT_FilterOption) => void; table: MRT_TableInstance; }) => ReactNode[]; + renderEmptyRowsFallback?: (props: { + table: MRT_TableInstance; + }) => ReactNode; renderRowActionMenuItems?: (props: { closeMenu: () => void; row: MRT_Row; diff --git a/packages/material-react-table/src/body/MRT_TableBody.tsx b/packages/material-react-table/src/body/MRT_TableBody.tsx index 01ee619d4..2491d88b5 100644 --- a/packages/material-react-table/src/body/MRT_TableBody.tsx +++ b/packages/material-react-table/src/body/MRT_TableBody.tsx @@ -42,6 +42,7 @@ export const MRT_TableBody = ({ manualSorting, memoMode, muiTableBodyProps, + renderEmptyRowsFallback, rowVirtualizerInstanceRef, rowVirtualizerProps, virtualizerInstanceRef, @@ -163,24 +164,28 @@ export const MRT_TableBody = ({ - - {globalFilter || columnFilters.length - ? localization.noResultsFound - : localization.noRecordsToDisplay} - + {renderEmptyRowsFallback?.({ table }) ?? ( + + {globalFilter || columnFilters.length + ? localization.noResultsFound + : localization.noRecordsToDisplay} + + )} ) : ( diff --git a/packages/material-react-table/src/body/MRT_TableBodyRow.tsx b/packages/material-react-table/src/body/MRT_TableBodyRow.tsx index 96eab5aec..da3036d88 100644 --- a/packages/material-react-table/src/body/MRT_TableBodyRow.tsx +++ b/packages/material-react-table/src/body/MRT_TableBodyRow.tsx @@ -47,7 +47,7 @@ export const MRT_TableBodyRow = ({ const tableRowProps = muiTableBodyRowProps instanceof Function - ? muiTableBodyRowProps({ row, table }) + ? muiTableBodyRowProps({ row, staticRowIndex: rowIndex, table }) : muiTableBodyRowProps; const handleDragEnter = (_e: DragEvent) => { @@ -136,6 +136,7 @@ export const MRT_TableBodyRow = ({ diff --git a/packages/material-react-table/src/body/MRT_TableDetailPanel.tsx b/packages/material-react-table/src/body/MRT_TableDetailPanel.tsx index 88fb4a95d..b5b1398ec 100644 --- a/packages/material-react-table/src/body/MRT_TableDetailPanel.tsx +++ b/packages/material-react-table/src/body/MRT_TableDetailPanel.tsx @@ -9,6 +9,7 @@ import type { MRT_Row, MRT_TableInstance } from '..'; interface Props { parentRowRef: React.RefObject; row: MRT_Row; + rowIndex: number; table: MRT_TableInstance; virtualRow?: VirtualItem; } @@ -16,6 +17,7 @@ interface Props { export const MRT_TableDetailPanel = ({ parentRowRef, row, + rowIndex, table, virtualRow, }: Props) => { @@ -33,7 +35,7 @@ export const MRT_TableDetailPanel = ({ const tableRowProps = muiTableBodyRowProps instanceof Function - ? muiTableBodyRowProps({ isDetailPanel: true, row, table }) + ? muiTableBodyRowProps({ isDetailPanel: true, row, staticRowIndex: rowIndex, table }) : muiTableBodyRowProps; const tableCellProps = diff --git a/packages/material-react-table/src/column.utils.ts b/packages/material-react-table/src/column.utils.ts index e80786461..a4d185b64 100644 --- a/packages/material-react-table/src/column.utils.ts +++ b/packages/material-react-table/src/column.utils.ts @@ -164,29 +164,34 @@ export const getTrailingDisplayColumnIds = < TData extends Record = {}, >( props: MaterialReactTableProps, -) => [ - props.positionActionsColumn === 'last' && - (props.enableRowActions || - (props.enableEditing && - ['row', 'modal'].includes(props.editingMode ?? ''))) && - 'mrt-row-actions', - props.positionExpandColumn === 'last' && - showExpandColumn(props) && - 'mrt-row-expand', -]; +) => + [ + props.positionActionsColumn === 'last' && + (props.enableRowActions || + (props.enableEditing && + ['row', 'modal'].includes(props.editingMode ?? ''))) && + 'mrt-row-actions', + props.positionExpandColumn === 'last' && + showExpandColumn(props) && + 'mrt-row-expand', + ].filter(Boolean) as MRT_DisplayColumnIds[]; export const getDefaultColumnOrderIds = < TData extends Record = {}, >( props: MaterialReactTableProps, -) => - [ - ...getLeadingDisplayColumnIds(props), - ...getAllLeafColumnDefs(props.columns).map((columnDef) => - getColumnId(columnDef), - ), - ...getTrailingDisplayColumnIds(props), - ].filter(Boolean) as string[]; +) => { + const leadingDisplayCols: string[] = getLeadingDisplayColumnIds(props); + const trailingDisplayCols: string[] = getTrailingDisplayColumnIds(props); + const allLeafColumnDefs = getAllLeafColumnDefs(props.columns) + .map((columnDef) => getColumnId(columnDef)) + .filter( + (columnId) => + !leadingDisplayCols.includes(columnId) && + !trailingDisplayCols.includes(columnId), + ); + return [...leadingDisplayCols, ...allLeafColumnDefs, ...trailingDisplayCols]; +}; export const getDefaultColumnFilterFn = < TData extends Record = {}, @@ -250,72 +255,79 @@ export const getCommonCellStyles = ({ 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, - display: table.options.layoutMode === 'grid' ? 'flex' : 'table-cell', - flex: - table.options.layoutMode === 'grid' - ? `var(--${header ? 'header' : 'col'}-${parseCSSVarId( - header?.id ?? column.id, - )}-size) 0 auto` - : undefined, - left: - column.getIsPinned() === 'left' - ? `${column.getStart('left')}px` - : undefined, - ml: - table.options.enableColumnVirtualization && - column.getIsPinned() === 'left' && - column.getPinnedIndex() === 0 - ? `-${ - column.getSize() * (table.getState().columnPinning.left?.length ?? 1) - }px` - : undefined, - mr: - table.options.enableColumnVirtualization && - column.getIsPinned() === 'right' && - column.getPinnedIndex() === table.getVisibleLeafColumns().length - 1 - ? `-${ - column.getSize() * - (table.getState().columnPinning.right?.length ?? 1) * - 1.2 - }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` +}) => { + const widthStyles = { + minWidth: `max(calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId( + header?.id ?? column.id, + )}-size) * 1px), ${column.columnDef.minSize ?? 30}px)`, + width: `calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId( + header?.id ?? column.id, + )}-size) * 1px)`, + }; + return { + 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, - transition: table.options.enableColumnVirtualization - ? 'none' - : `padding 150ms ease-in-out`, - ...(tableCellProps?.sx instanceof Function - ? tableCellProps.sx(theme) - : (tableCellProps?.sx as any)), - minWidth: `max(calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId( - header?.id ?? column.id, - )}-size) * 1px), ${column.columnDef.minSize ?? 30}px)`, - width: `calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId( - header?.id ?? column.id, - )}-size) * 1px)`, -}); + display: table.options.layoutMode === 'grid' ? 'flex' : 'table-cell', + flex: + table.options.layoutMode === 'grid' + ? `var(--${header ? 'header' : 'col'}-${parseCSSVarId( + header?.id ?? column.id, + )}-size) 0 auto` + : undefined, + left: + column.getIsPinned() === 'left' + ? `${column.getStart('left')}px` + : undefined, + ml: + table.options.enableColumnVirtualization && + column.getIsPinned() === 'left' && + column.getPinnedIndex() === 0 + ? `-${ + column.getSize() * + (table.getState().columnPinning.left?.length ?? 1) + }px` + : undefined, + mr: + table.options.enableColumnVirtualization && + column.getIsPinned() === 'right' && + column.getPinnedIndex() === table.getVisibleLeafColumns().length - 1 + ? `-${ + column.getSize() * + (table.getState().columnPinning.right?.length ?? 1) * + 1.2 + }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: table.options.enableColumnVirtualization + ? 'none' + : `padding 150ms ease-in-out`, + ...(!table.options.enableColumnResizing && widthStyles), //let devs pass in width styles if column resizing is disabled + ...(tableCellProps?.sx instanceof Function + ? tableCellProps.sx(theme) + : (tableCellProps?.sx as any)), + ...(table.options.enableColumnResizing && widthStyles), //don't let devs pass in width styles if column resizing is enabled + }; +}; export const MRT_DefaultColumn = { filterVariant: 'text', diff --git a/packages/material-react-table/src/toolbar/MRT_ToolbarDropZone.tsx b/packages/material-react-table/src/toolbar/MRT_ToolbarDropZone.tsx index fd9b1f6b5..ba6b2252c 100644 --- a/packages/material-react-table/src/toolbar/MRT_ToolbarDropZone.tsx +++ b/packages/material-react-table/src/toolbar/MRT_ToolbarDropZone.tsx @@ -31,6 +31,7 @@ export const MRT_ToolbarDropZone = = {}>({ setShowToolbarDropZone( !!enableGrouping && !!draggingColumn && + draggingColumn.columnDef.enableGrouping !== false && !grouping.includes(draggingColumn.id), ); }