diff --git a/apps/material-react-table-docs/components/mdx/SourceCodeSnippet.tsx b/apps/material-react-table-docs/components/mdx/SourceCodeSnippet.tsx
index b4947dfcc..a9aafdcfe 100644
--- a/apps/material-react-table-docs/components/mdx/SourceCodeSnippet.tsx
+++ b/apps/material-react-table-docs/components/mdx/SourceCodeSnippet.tsx
@@ -36,7 +36,6 @@ import { EthicalAd } from './EthicalAd';
export interface Props {
Component?: any;
- apiCode?: string;
tableId: string;
typeScriptCode: string;
showTopRow?: boolean;
@@ -44,7 +43,6 @@ export interface Props {
export const SourceCodeSnippet = ({
Component,
- apiCode,
tableId,
typeScriptCode,
showTopRow = true,
@@ -58,6 +56,7 @@ export const SourceCodeSnippet = ({
setSecondaryColor,
primaryColor,
setPrimaryColor,
+ setIsSandboxOpen,
} = useThemeContext();
const isMobile = useMediaQuery('(max-width: 720px)');
const [codeTab, setCodeTab] = useState<
@@ -75,6 +74,13 @@ export const SourceCodeSnippet = ({
}
}, []);
+ useEffect(() => {
+ setIsSandboxOpen(['stackblitz', 'sandbox'].includes(codeTab));
+ return () => {
+ setIsSandboxOpen(false);
+ };
+ }, [codeTab]);
+
const handleDismissV2Alert = () => {
localStorage.setItem('v2AlertDismissed', 'true');
setShowV2Alert(false);
@@ -82,9 +88,7 @@ export const SourceCodeSnippet = ({
};
const handleCopy = () => {
- navigator.clipboard.writeText(
- (codeTab === 'ts' ? typeScriptCode : apiCode) ?? '',
- );
+ navigator.clipboard.writeText(typeScriptCode ?? '');
setIsCopied(true);
setTimeout(() => setIsCopied(false), 3000);
};
@@ -227,7 +231,9 @@ export const SourceCodeSnippet = ({
)}
-
+
+
+
>
)}
@@ -291,19 +297,6 @@ export const SourceCodeSnippet = ({
>
{isMobile ? 'TS' : 'TypeScript'}
- {apiCode && (
-
{
- setCodeTab('api');
- plausible('toggle-to-api');
- }}
- value="api"
- selected={codeTab === 'api'}
- sx={{ textTransform: 'none' }}
- >
- {isMobile ? 'API' : 'Back-end API'}
-
- )}
{
setCodeTab('stackblitz');
@@ -340,6 +333,7 @@ export const SourceCodeSnippet = ({
border: '0',
borderRadius: '4px',
overflow: 'hidden',
+ padding: '1rem 0',
}}
title="stackblitz"
allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
@@ -357,6 +351,7 @@ export const SourceCodeSnippet = ({
border: '0',
borderRadius: '4px',
overflow: 'hidden',
+ padding: '1rem 0',
}}
title="codesandbox"
allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
@@ -366,7 +361,7 @@ export const SourceCodeSnippet = ({
{['ts', 'api'].includes(codeTab) && (
{
+ const { pathname } = useRouter();
+
+ const { isSandboxOpen } = useThemeContext();
+
+ const showBreadCrumbs = pathname !== '/';
+ const showMiniNav =
+ pathname.includes('/docs/getting-started') ||
+ pathname.includes('/docs/api/mrt') ||
+ pathname.includes('/docs/guides/') ||
+ pathname.includes('/migrating') ||
+ pathname === '/about' ||
+ pathname === '/changelog' ||
+ pathname === '/roadmap';
+
+ const isMobile = useMediaQuery('(max-width: 900px)');
+ const isDesktop = useMediaQuery('(min-width: 1500px)');
+ const isXLDesktop = useMediaQuery('(min-width: 1800px)');
+
+ const [navOpen, setNavOpen] = useState(false);
+ return (
+ <>
+
+
+ ({
+ backgroundColor: theme.palette.background.default,
+ color: theme.palette.text.primary,
+ minHeight: '100vh',
+ p: `64px ${showMiniNav && isXLDesktop ? '264px' : '32px'} 0 ${
+ (navOpen || (isDesktop && !isSandboxOpen)) && !isMobile
+ ? '320px'
+ : '32px'
+ }`,
+ transition: 'all 100ms ease-in-out',
+ })}
+ >
+
+ {showBreadCrumbs && }
+ {showMiniNav && !isXLDesktop && }
+ {pathname === '/' ? children : {children}}
+
+
+ {showMiniNav && isXLDesktop && }
+
+ >
+ );
+};
diff --git a/apps/material-react-table-docs/components/prop-tables/tableOptions.ts b/apps/material-react-table-docs/components/prop-tables/tableOptions.ts
index 3159ecbe6..aff09eff2 100644
--- a/apps/material-react-table-docs/components/prop-tables/tableOptions.ts
+++ b/apps/material-react-table-docs/components/prop-tables/tableOptions.ts
@@ -493,7 +493,7 @@ export const tableOptions: TableOption[] = [
type: 'boolean',
},
{
- tableOption: 'enableCellNavigation',
+ tableOption: 'enableKeyboardShortcuts',
defaultValue: 'true',
description: '',
link: '',
diff --git a/apps/material-react-table-docs/examples/minimal/sandbox/src/TS.tsx b/apps/material-react-table-docs/examples/minimal/sandbox/src/TS.tsx
index e64949707..a4b3768d1 100644
--- a/apps/material-react-table-docs/examples/minimal/sandbox/src/TS.tsx
+++ b/apps/material-react-table-docs/examples/minimal/sandbox/src/TS.tsx
@@ -38,7 +38,7 @@ export const Example = () => {
const table = useMaterialReactTable({
columns,
data, //data must be memoized or stable (useState, useMemo, defined outside of this component, etc.)
- enableCellNavigation: false,
+ enableKeyboardShortcuts: false,
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
diff --git a/apps/material-react-table-docs/pages/_app.tsx b/apps/material-react-table-docs/pages/_app.tsx
index d4414c104..846ba20ba 100644
--- a/apps/material-react-table-docs/pages/_app.tsx
+++ b/apps/material-react-table-docs/pages/_app.tsx
@@ -1,38 +1,16 @@
import '../styles/globals.css';
-import { useState } from 'react';
import { type AppProps } from 'next/app';
import Head from 'next/head';
import PlausibleProvider from 'next-plausible';
import { useRouter } from 'next/router';
import { MDXProvider } from '@mdx-js/react';
-import { Box, useMediaQuery } from '@mui/material';
import { ThemeContextProvider } from '../styles/ThemeContext';
import { mdxComponents } from '../components/mdx/mdxComponents';
-import { TopBar } from '../components/navigation/TopBar';
-import { SideBar } from '../components/navigation/Sidebar';
-import { BreadCrumbs } from '../components/navigation/BreadCrumbs';
-import { MiniNav } from '../components/navigation/MiniNav';
-import { Footer } from '../components/navigation/Footer';
+import { Layout } from '../components/navigation/Layout';
function App({ Component, pageProps }: AppProps) {
const { pathname } = useRouter();
- const showBreadCrumbs = pathname !== '/';
- const showMiniNav =
- pathname.includes('/docs/getting-started') ||
- pathname.includes('/docs/api/mrt') ||
- pathname.includes('/docs/guides/') ||
- pathname.includes('/migrating') ||
- pathname === '/about' ||
- pathname === '/changelog' ||
- pathname === '/roadmap';
-
- const isMobile = useMediaQuery('(max-width: 900px)');
- const isDesktop = useMediaQuery('(min-width: 1500px)');
- const isXLDesktop = useMediaQuery('(min-width: 1800px)');
-
- const [navOpen, setNavOpen] = useState(false);
-
return (
<>
@@ -71,41 +49,9 @@ function App({ Component, pageProps }: AppProps) {
>
-
-
- ({
- backgroundColor: theme.palette.background.default,
- color: theme.palette.text.primary,
- minHeight: '100vh',
- p: `64px ${showMiniNav && isXLDesktop ? '250px' : '32px'} 0 ${
- (navOpen || isDesktop) && !isMobile ? '320px' : '32px'
- }`,
- transition: 'all 100ms ease-in-out',
- })}
- >
-
- {showBreadCrumbs && }
- {showMiniNav && !isXLDesktop && }
- {pathname === '/' ? (
-
- ) : (
-
-
-
- )}
-
-
- {showMiniNav && isXLDesktop && }
-
+
+
+
diff --git a/apps/material-react-table-docs/pages/docs/getting-started/migrating-to-v3.mdx b/apps/material-react-table-docs/pages/docs/getting-started/migrating-to-v3.mdx
index 47f6b8ae3..743bad0f3 100644
--- a/apps/material-react-table-docs/pages/docs/getting-started/migrating-to-v3.mdx
+++ b/apps/material-react-table-docs/pages/docs/getting-started/migrating-to-v3.mdx
@@ -55,6 +55,7 @@ You should now be on Material React Table V3! Look for any code or type errors i
- `@mui/material` and `@mui/icons-material` v6.0.0 are now minimum required versions of Material UI packages (you might be able to get away with lower MUI versions for a while, but eventually MUI V6 APIs will be used internally by MRT and your project will break)
- `@mui/x-date-pickers` v7.15.0 is now a minimum required dependency
+- Keyboard navigation for table cells in now enabled by default. If you had added your own custom keyboard shortcuts, you may want to set `enableKeyboardShortcuts` to `false` or remove your custom shortcuts.
- Removed deprecated `MRT_Virtualizer` type in favor of separate `MRT_RowVirtualizer` and `MRT_ColumnVirtualizer` types
- Removed deprecated `text` in favor of the more consistent `label` type in dropdown/autocomplete/select option types.
- Deprecated several `mui*Props` table options that were column-specific. These table options should either be specified in column defs or in the `defaultColumn` table option.
diff --git a/apps/material-react-table-docs/pages/docs/guides/accessibility.mdx b/apps/material-react-table-docs/pages/docs/guides/accessibility.mdx
index 86a5f7245..dd429aa2d 100644
--- a/apps/material-react-table-docs/pages/docs/guides/accessibility.mdx
+++ b/apps/material-react-table-docs/pages/docs/guides/accessibility.mdx
@@ -15,16 +15,111 @@ import StateOptionsTable from '../../../components/prop-tables/StateOptionsTable
## Accessibility / Keyboard Navigation Guide
-Material React Table tries to get the basics of data grid accessibility right out of the box. But since you can easily add event handlers to just about any interaction inside of the table, you can heavily customize the accessibility of your table to your needs.
+Material React Table tries to get the basics of data grid accessibility right out of the box. But since you can easily add event handlers to just about any interaction inside of the table, you can heavily customize the accessibility of your table to your needs too.
### Relevant Table Options
-
+
### Keyboard Navigation
> New in v3
-Material React Table uses the `tab` key to move focus between cells, rows, and columns.
+Material React Table V3 introduces a new table option that is `true` by default: `enableKeyboardShortcuts`
+
+#### `enableKeyboardShortcuts`
+
+This option enables the following keyboard shortcuts:
+
+- `Tab` - Moves the focus to the next cell or element in the table (standard browser behavior)
+- `Arrow Up` - Moves the focus 1 cell up
+- `Arrow Down` - Moves the focus 1 cell down
+- `Arrow Left` - Moves the focus 1 cell left
+- `Arrow Right` - Moves the focus 1 cell right
+- `Home` - Moves the focus to the first cell in the current row
+- `End` - Moves the focus to the last cell in the current row
+- `Page Up` - Moves the focus to the first cell in the current column
+- `Page Down` - Moves the focus to the last cell in the current column
+- `Ctrl/Cmd + Home` - Moves the focus to the first cell in the table (top left in left-to-right languages)
+- `Ctrl/Cmd + End` - Moves the focus to the last cell in the table (bottom right in left-to-right languages)
+- `Enter` - Performs certain actions in the currently focused cell such as sorting, row selection, row expansion, row pinning, etc.
+- `Space` - Also performs certain actions in the currently focused cell such as sorting, row selection, row expansion, row pinning, etc.
+- `Ctrl/Cmd + Enter` - Opens column actions menu on a header
+
+A `tabIndex={0}` is also automatically added to all cells, headers, and footers when `enableKeyboardShortcuts` is enabled to allow for keyboard focus.
+
+### Custom Keyboard Shortcuts
+
+You do not have to use the provided keyboard shortcuts. You can turn off `enableKeyboardShortcuts` and add your own keyboard shortcuts using the `onKeyDown` event handler on all of the table cells (or any other element).
+
+You can also add custom focus styles to any element using the `sx` prop.
+
+```tsx
+const table = useMaterialReactTable({
+ columns,
+ data,
+ //add custom keyboard shortcuts
+ defaultColumn: {
+ //header
+ muiTableHeadCellProps: {
+ onKeyDown: (event) => {
+ if (event.key === 't' && event.metaKey) {
+ alert('You pressed the custom shortcut!');
+ }
+ },
+ tabIndex: 0, //allow for keyboard focus
+ },
+ //body
+ muiTableBodyCellProps: {
+ onKeyDown: (event) => {
+ if (event.key === 't' && event.metaKey) {
+ alert('You pressed the custom shortcut!');
+ }
+ },
+ //add custom focus styles
+ sx: {
+ '&:focus-visible': {
+ //or just `&:focus` if you want all focus events to be visible
+ outline: '2px solid red',
+ outlineOffset: '-2px',
+ },
+ },
+ tabIndex: 0, //allow for keyboard focus
+ },
+ },
+ enableKeyboardShortcuts: false, //turn off default keyboard shortcuts from MRT
+});
+```
+
+### Custom Focus Styles
+
+An outline is automatically added to all elements when they are focused. You can easily customize the color of the outline by setting `cellNavigationOutlineColor` property in the `mrtTheme` table option.
+
+```tsx
+const table = useMaterialReactTable({
+ columns,
+ data,
+ mrtTheme: {
+ cellNavigationOutlineColor 'limegreen'
+ }
+});
+```
+
+Or, of course, you can just use the `sx` props to override any styles you want, just like any other style override in MRT.
+
+```tsx
+const table = useMaterialReactTable({
+ columns,
+ data,
+ defaultColumn: {
+ muiTableBodyCellProps: {
+ sx: {
+ '&:focus-visible': {
+ outline: '4px solid red',
+ outlineOffset: '-4px',
+ },
+ },
+ },
+ },
+});
+```
diff --git a/apps/material-react-table-docs/pages/roadmap.mdx b/apps/material-react-table-docs/pages/roadmap.mdx
index 9d5c0e03e..e36e937c7 100644
--- a/apps/material-react-table-docs/pages/roadmap.mdx
+++ b/apps/material-react-table-docs/pages/roadmap.mdx
@@ -25,6 +25,6 @@ The future versions of Material React Table will include much bigger upgrades an
- Upgrade to TanStack Table V9 and rewrite MRT to have more modular options. (Big changes for massive decrease in bundle size, but old way of using MRT will still be supported)
- Add Joy and Base UI adapters.
-#### 3. Mantine React Table
+#### Mantine React Table
Material React Table's sister library,[ Mantine React Table](https://mantine-react-table.com), continues to be developed in parallel, as it is nearing a stable V3 release itself. If you like MRT, but prefer to write pure CSS/CSS Modules instead of styled components or the `sx` prop, you'll probably like Mantine React Table more than Material React Table.
diff --git a/apps/material-react-table-docs/styles/ThemeContext.tsx b/apps/material-react-table-docs/styles/ThemeContext.tsx
index 0decced62..b725436d9 100644
--- a/apps/material-react-table-docs/styles/ThemeContext.tsx
+++ b/apps/material-react-table-docs/styles/ThemeContext.tsx
@@ -11,6 +11,8 @@ const ThemeContext = createContext<{
setPrimaryColor: (primaryColor: string | undefined) => void;
secondaryColor: string;
setSecondaryColor: (secondaryColor: string) => void;
+ isSandboxOpen: boolean;
+ setIsSandboxOpen: (isSandboxOpen: boolean) => void;
}>({} as any);
export const ThemeContextProvider = ({ children }) => {
@@ -18,6 +20,7 @@ export const ThemeContextProvider = ({ children }) => {
const [primaryColor, setPrimaryColor] = useState();
const [secondaryColor, setSecondaryColor] =
useState('rgb(20,184,166)');
+ const [isSandboxOpen, setIsSandboxOpen] = useState(false);
useEffect(() => {
if (typeof window !== 'undefined') {
@@ -41,6 +44,8 @@ export const ThemeContextProvider = ({ children }) => {
setPrimaryColor,
secondaryColor,
setSecondaryColor,
+ isSandboxOpen,
+ setIsSandboxOpen,
}}
>
diff --git a/packages/material-react-table/package.json b/packages/material-react-table/package.json
index 5df2f6b1f..7cb9e972c 100644
--- a/packages/material-react-table/package.json
+++ b/packages/material-react-table/package.json
@@ -1,5 +1,5 @@
{
- "version": "3.0.0-beta.1",
+ "version": "3.0.0-beta.2",
"license": "MIT",
"name": "material-react-table",
"description": "A fully featured Material UI V6 implementation of TanStack React Table V8, written from the ground up in TypeScript.",
diff --git a/packages/material-react-table/src/components/body/MRT_TableBodyCell.tsx b/packages/material-react-table/src/components/body/MRT_TableBodyCell.tsx
index 950c02f9d..666cc0d7a 100644
--- a/packages/material-react-table/src/components/body/MRT_TableBodyCell.tsx
+++ b/packages/material-react-table/src/components/body/MRT_TableBodyCell.tsx
@@ -18,7 +18,7 @@ import {
} from '../../types';
import {
isCellEditable,
- cellNavigation,
+ cellKeyboardShortcuts,
openEditingCell,
} from '../../utils/cell.utils';
import { getCommonMRTCellStyles } from '../../utils/style.utils';
@@ -58,7 +58,7 @@ export const MRT_TableBodyCell = ({
enableColumnOrdering,
enableColumnPinning,
enableGrouping,
- enableCellNavigation,
+ enableKeyboardShortcuts,
layoutMode,
mrtTheme: { draggingBorderColor },
muiSkeletonProps,
@@ -233,14 +233,12 @@ export const MRT_TableBodyCell = ({
};
const handleKeyDown = (event: React.KeyboardEvent) => {
- if (enableCellNavigation) {
- cellNavigation({
- cell,
- cellValue: cell.getValue(),
- event,
- table,
- });
- }
+ cellKeyboardShortcuts({
+ cell,
+ cellValue: cell.getValue(),
+ event,
+ table,
+ });
tableCellProps?.onKeyDown?.(event);
};
@@ -249,7 +247,7 @@ export const MRT_TableBodyCell = ({
align={theme.direction === 'rtl' ? 'right' : 'left'}
data-index={staticColumnIndex}
data-pinned={!!isColumnPinned || undefined}
- tabIndex={enableCellNavigation ? 0 : undefined}
+ tabIndex={enableKeyboardShortcuts ? 0 : undefined}
{...tableCellProps}
onKeyDown={handleKeyDown}
onContextMenu={handleContextMenu}
diff --git a/packages/material-react-table/src/components/footer/MRT_TableFooterCell.tsx b/packages/material-react-table/src/components/footer/MRT_TableFooterCell.tsx
index a70419297..77111e5b1 100644
--- a/packages/material-react-table/src/components/footer/MRT_TableFooterCell.tsx
+++ b/packages/material-react-table/src/components/footer/MRT_TableFooterCell.tsx
@@ -7,7 +7,7 @@ import {
} from '../../types';
import { getCommonMRTCellStyles } from '../../utils/style.utils';
import { parseFromValuesOrFunc } from '../../utils/utils';
-import { cellNavigation } from '../../utils/cell.utils';
+import { cellKeyboardShortcuts } from '../../utils/cell.utils';
export interface MRT_TableFooterCellProps
extends TableCellProps {
@@ -28,7 +28,7 @@ export const MRT_TableFooterCell = ({
options: {
enableColumnPinning,
muiTableFooterCellProps,
- enableCellNavigation,
+ enableKeyboardShortcuts,
},
} = table;
const { density } = getState();
@@ -49,13 +49,11 @@ export const MRT_TableFooterCell = ({
};
const handleKeyDown = (event: React.KeyboardEvent) => {
- if (enableCellNavigation) {
- cellNavigation({
- event,
- cellValue: footer.column.columnDef.footer,
- table,
- });
- }
+ cellKeyboardShortcuts({
+ event,
+ cellValue: footer.column.columnDef.footer,
+ table,
+ });
tableCellProps?.onKeyDown?.(event);
};
@@ -71,6 +69,7 @@ export const MRT_TableFooterCell = ({
colSpan={footer.colSpan}
data-index={staticColumnIndex}
data-pinned={!!isColumnPinned || undefined}
+ tabIndex={enableKeyboardShortcuts ? 0 : undefined}
variant="footer"
{...tableCellProps}
onKeyDown={handleKeyDown}
diff --git a/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx b/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx
index 1952de343..9bf270933 100644
--- a/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx
+++ b/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx
@@ -17,7 +17,7 @@ import {
} from '../../types';
import { getCommonMRTCellStyles } from '../../utils/style.utils';
import { parseFromValuesOrFunc } from '../../utils/utils';
-import { cellNavigation } from '../../utils/cell.utils';
+import { cellKeyboardShortcuts } from '../../utils/cell.utils';
export interface MRT_TableHeadCellProps
extends TableCellProps {
@@ -41,12 +41,12 @@ export const MRT_TableHeadCell = ({
columnFilterDisplayMode,
columnResizeDirection,
columnResizeMode,
+ enableKeyboardShortcuts,
enableColumnActions,
enableColumnDragging,
enableColumnOrdering,
enableColumnPinning,
enableGrouping,
- enableCellNavigation,
enableMultiSort,
layoutMode,
mrtTheme: { draggingBorderColor },
@@ -150,14 +150,13 @@ export const MRT_TableHeadCell = ({
};
const handleKeyDown = (event: React.KeyboardEvent) => {
- if (enableCellNavigation) {
- cellNavigation({
- event,
- cellValue: header.column.columnDef.header,
- table,
- header,
- });
- }
+ cellKeyboardShortcuts({
+ event,
+ cellValue: header.column.columnDef.header,
+ table,
+ header,
+ });
+
tableCellProps?.onKeyDown?.(event);
};
@@ -199,7 +198,7 @@ export const MRT_TableHeadCell = ({
}
}
}}
- tabIndex={enableCellNavigation ? 0 : undefined}
+ tabIndex={enableKeyboardShortcuts ? 0 : undefined}
{...tableCellProps}
onKeyDown={handleKeyDown}
sx={(theme: Theme) => ({
diff --git a/packages/material-react-table/src/hooks/useMRT_TableOptions.ts b/packages/material-react-table/src/hooks/useMRT_TableOptions.ts
index fb6bcbb7a..ca14a0ce9 100644
--- a/packages/material-react-table/src/hooks/useMRT_TableOptions.ts
+++ b/packages/material-react-table/src/hooks/useMRT_TableOptions.ts
@@ -76,7 +76,7 @@ export const useMRT_TableOptions: (
enableGlobalFilterRankedResults = true,
enableGrouping = false,
enableHiding = true,
- enableCellNavigation = true,
+ enableKeyboardShortcuts = true,
enableMultiRowSelection = true,
enableMultiSort = true,
enablePagination = true,
@@ -204,7 +204,7 @@ export const useMRT_TableOptions: (
enableGlobalFilterRankedResults,
enableGrouping,
enableHiding,
- enableCellNavigation,
+ enableKeyboardShortcuts,
enableMultiRowSelection,
enableMultiSort,
enablePagination,
diff --git a/packages/material-react-table/src/types.ts b/packages/material-react-table/src/types.ts
index 7611d1fd8..94370d8e0 100644
--- a/packages/material-react-table/src/types.ts
+++ b/packages/material-react-table/src/types.ts
@@ -184,9 +184,10 @@ export interface MRT_Localization {
filterFuzzy: string;
filterGreaterThan: string;
filterGreaterThanOrEqualTo: string;
- filterInNumberRange: string;
filterIncludesString: string;
filterIncludesStringSensitive: string;
+ filteringByColumn: string;
+ filterInNumberRange: string;
filterLessThan: string;
filterLessThanOrEqualTo: string;
filterMode: string;
@@ -194,7 +195,6 @@ export interface MRT_Localization {
filterNotEquals: string;
filterStartsWith: string;
filterWeakEquals: string;
- filteringByColumn: string;
goToFirstPage: string;
goToLastPage: string;
goToNextPage: string;
@@ -412,6 +412,22 @@ export interface MRT_ColumnDef
| 'id'
| 'sortingFn'
> {
+ /**
+ * Either an `accessorKey` or a combination of an `accessorFn` and `id` are required for a data column definition.
+ * Specify a function here to point to the correct property in the data object.
+ *
+ * @example accessorFn: (row) => row.username
+ */
+ accessorFn?: (originalRow: TData) => TValue;
+ /**
+ * Either an `accessorKey` or a combination of an `accessorFn` and `id` are required for a data column definition.
+ * Specify which key in the row this column should use to access the correct data.
+ * Also supports Deep Key Dot Notation.
+ *
+ * @example accessorKey: 'username' //simple
+ * @example accessorKey: 'name.firstName' //deep key dot notation
+ */
+ accessorKey?: DeepKeys | (string & {});
AggregatedCell?: (props: {
cell: MRT_Cell;
column: MRT_Column;
@@ -420,6 +436,7 @@ export interface MRT_ColumnDef
staticColumnIndex?: number;
staticRowIndex?: number;
}) => ReactNode;
+ aggregationFn?: Array> | MRT_AggregationFn;
Cell?: (props: {
cell: MRT_Cell;
column: MRT_Column;
@@ -430,63 +447,6 @@ export interface MRT_ColumnDef
staticRowIndex?: number;
table: MRT_TableInstance;
}) => ReactNode;
- Edit?: (props: {
- cell: MRT_Cell;
- column: MRT_Column;
- row: MRT_Row;
- table: MRT_TableInstance;
- }) => ReactNode;
- Filter?: (props: {
- column: MRT_Column;
- header: MRT_Header;
- rangeFilterIndex?: number;
- table: MRT_TableInstance;
- }) => ReactNode;
- Footer?:
- | ((props: {
- column: MRT_Column;
- footer: MRT_Header;
- table: MRT_TableInstance;
- }) => ReactNode)
- | ReactNode;
- GroupedCell?: (props: {
- cell: MRT_Cell;
- column: MRT_Column;
- row: MRT_Row;
- table: MRT_TableInstance;
- staticColumnIndex?: number;
- staticRowIndex?: number;
- }) => ReactNode;
- Header?:
- | ((props: {
- column: MRT_Column;
- header: MRT_Header;
- table: MRT_TableInstance;
- }) => ReactNode)
- | ReactNode;
- PlaceholderCell?: (props: {
- cell: MRT_Cell;
- column: MRT_Column;
- row: MRT_Row;
- table: MRT_TableInstance;
- }) => ReactNode;
- /**
- * Either an `accessorKey` or a combination of an `accessorFn` and `id` are required for a data column definition.
- * Specify a function here to point to the correct property in the data object.
- *
- * @example accessorFn: (row) => row.username
- */
- accessorFn?: (originalRow: TData) => TValue;
- /**
- * Either an `accessorKey` or a combination of an `accessorFn` and `id` are required for a data column definition.
- * Specify which key in the row this column should use to access the correct data.
- * Also supports Deep Key Dot Notation.
- *
- * @example accessorKey: 'username' //simple
- * @example accessorKey: 'name.firstName' //deep key dot notation
- */
- accessorKey?: DeepKeys | (string & {});
- aggregationFn?: Array> | MRT_AggregationFn;
/**
* Specify what type of column this is. Either `data`, `display`, or `group`. Defaults to `data`.
* Leave this blank if you are just creating a normal data column.
@@ -500,6 +460,12 @@ export interface MRT_ColumnDef
LiteralUnion
> | null;
columns?: MRT_ColumnDef[];
+ Edit?: (props: {
+ cell: MRT_Cell;
+ column: MRT_Column;
+ row: MRT_Row;
+ table: MRT_TableInstance;
+ }) => ReactNode;
editSelectOptions?:
| ((props: {
cell: MRT_Cell;
@@ -519,6 +485,12 @@ export interface MRT_ColumnDef
enableColumnOrdering?: boolean;
enableEditing?: ((row: MRT_Row) => boolean) | boolean;
enableFilterMatchHighlighting?: boolean;
+ Filter?: (props: {
+ column: MRT_Column;
+ header: MRT_Header;
+ rangeFilterIndex?: number;
+ table: MRT_TableInstance;
+ }) => ReactNode;
filterFn?: MRT_FilterFn;
filterSelectOptions?: DropdownOption[];
filterVariant?:
@@ -539,6 +511,21 @@ export interface MRT_ColumnDef
* footer must be a string. If you want custom JSX to render the footer, you can also specify a `Footer` option. (Capital F)
*/
footer?: string;
+ Footer?:
+ | ((props: {
+ column: MRT_Column;
+ footer: MRT_Header;
+ table: MRT_TableInstance;
+ }) => ReactNode)
+ | ReactNode;
+ GroupedCell?: (props: {
+ cell: MRT_Cell;
+ column: MRT_Column;
+ row: MRT_Row;
+ table: MRT_TableInstance;
+ staticColumnIndex?: number;
+ staticRowIndex?: number;
+ }) => ReactNode;
/**
* If `layoutMode` is `'grid'` or `'grid-no-grow'`, you can specify the flex grow value for individual columns to still grow and take up remaining space, or set to `false`/0 to not grow.
*/
@@ -547,6 +534,13 @@ export interface MRT_ColumnDef
* header must be a string. If you want custom JSX to render the header, you can also specify a `Header` option. (Capital H)
*/
header: string;
+ Header?:
+ | ((props: {
+ column: MRT_Column;
+ header: MRT_Header;
+ table: MRT_TableInstance;
+ }) => ReactNode)
+ | ReactNode;
/**
* Either an `accessorKey` or a combination of an `accessorFn` and `id` are required for a data column definition.
*
@@ -651,6 +645,12 @@ export interface MRT_ColumnDef
table: MRT_TableInstance;
}) => TableCellProps)
| TableCellProps;
+ PlaceholderCell?: (props: {
+ cell: MRT_Cell;
+ column: MRT_Column;
+ row: MRT_Row;
+ table: MRT_TableInstance;
+ }) => ReactNode;
renderCellActionMenuItems?: (props: {
cell: MRT_Cell;
closeMenu: () => void;
@@ -813,12 +813,6 @@ export interface MRT_TableOptions
columnFilterModeOptions?: Array<
LiteralUnion
> | null;
- columnVirtualizerInstanceRef?: MutableRefObject;
- columnVirtualizerOptions?:
- | ((props: {
- table: MRT_TableInstance;
- }) => Partial>)
- | Partial>;
/**
* The columns to display in the table. `accessorKey`s or `accessorFn`s must match keys in the `data` table option.
*
@@ -830,6 +824,12 @@ export interface MRT_TableOptions
* @link https://www.material-react-table.com/docs/api/column-options
*/
columns: MRT_ColumnDef[];
+ columnVirtualizerInstanceRef?: MutableRefObject;
+ columnVirtualizerOptions?:
+ | ((props: {
+ table: MRT_TableInstance;
+ }) => Partial>)
+ | Partial>;
createDisplayMode?: 'custom' | 'modal' | 'row';
/**
* Pass your data as an array of objects. Objects can theoretically be any shape, but it's best to keep them consistent.
@@ -870,7 +870,7 @@ export interface MRT_TableOptions
enableFullScreenToggle?: boolean;
enableGlobalFilterModes?: boolean;
enableGlobalFilterRankedResults?: boolean;
- enableCellNavigation?: boolean;
+ enableKeyboardShortcuts?: boolean;
enablePagination?: boolean;
enableRowActions?: boolean;
enableRowDragging?: boolean;
diff --git a/packages/material-react-table/src/utils/cell.utils.ts b/packages/material-react-table/src/utils/cell.utils.ts
index d387c9c93..6b612f0b2 100644
--- a/packages/material-react-table/src/utils/cell.utils.ts
+++ b/packages/material-react-table/src/utils/cell.utils.ts
@@ -10,6 +10,13 @@ import {
} from './row.utils';
import { parseFromValuesOrFunc } from './utils';
+const isWinCtrlMacMeta = (event: React.KeyboardEvent) => {
+ return (
+ (event.ctrlKey && navigator.platform.toLowerCase().includes('win')) ||
+ (event.metaKey && navigator.platform.toLowerCase().includes('mac'))
+ );
+};
+
export const isCellEditable = ({
cell,
table,
@@ -54,7 +61,7 @@ export const openEditingCell = ({
}
};
-export const cellNavigation = ({
+export const cellKeyboardShortcuts = ({
cell,
cellElements,
cellValue,
@@ -73,10 +80,14 @@ export const cellNavigation = ({
parentElement?: HTMLTableRowElement;
table: MRT_TableInstance;
}) => {
- if (cellValue && (event.ctrlKey || event.metaKey) && event.key === 'c') {
+ if (!table.options.enableKeyboardShortcuts) return;
+ const currentCell = event.currentTarget;
+
+ if (cellValue && isWinCtrlMacMeta(event) && event.key === 'c') {
navigator.clipboard.writeText(cellValue);
} else if (['Enter', ' '].includes(event.key)) {
if (cell?.column?.id === 'mrt-row-select') {
+ event.preventDefault();
getMRT_RowSelectionHandler({
row: cell.row,
table,
@@ -87,6 +98,7 @@ export const cellNavigation = ({
header?.column?.id === 'mrt-row-select' &&
table.options.enableSelectAll
) {
+ event.preventDefault();
getMRT_SelectAllHandler({
table,
})(event as any);
@@ -95,15 +107,16 @@ export const cellNavigation = ({
(cell.row.getCanExpand() ||
table.options.renderDetailPanel?.({ row: cell.row, table }))
) {
+ event.preventDefault();
cell.row.toggleExpanded();
} else if (
header?.column?.id === 'mrt-row-expand' &&
table.options.enableExpandAll
) {
+ event.preventDefault();
table.toggleAllRowsExpanded();
- } else if (header?.column?.getCanSort()) {
- header.column.toggleSorting();
} else if (cell?.column.id === 'mrt-row-pin') {
+ event.preventDefault();
cell.row.getIsPinned()
? cell.row.pin(false)
: cell.row.pin(
@@ -111,16 +124,32 @@ export const cellNavigation = ({
? 'bottom'
: 'top',
);
+ } else if (header && isWinCtrlMacMeta(event)) {
+ const actionsButton = currentCell.querySelector(
+ `button[aria-label="${table.options.localization.columnActions}"]`,
+ );
+ if (actionsButton) {
+ (actionsButton as HTMLButtonElement).click();
+ }
+ } else if (header?.column?.getCanSort()) {
+ event.preventDefault();
+ header.column.toggleSorting();
}
} else if (
- ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(
- event.key,
- )
+ [
+ 'ArrowRight',
+ 'ArrowLeft',
+ 'ArrowUp',
+ 'ArrowDown',
+ 'Home',
+ 'End',
+ 'PageUp',
+ 'PageDown',
+ ].includes(event.key)
) {
event.preventDefault();
- const currentCell = event.currentTarget;
- const currentRow = parentElement || currentCell.closest('tr');
+ const currentRow = parentElement || currentCell.closest('tr');
const tableElement = containerElement || currentCell.closest('table');
const allCells =
cellElements ||
@@ -146,6 +175,17 @@ export const cellNavigation = ({
return targetCell as HTMLElement;
};
+ //page up/down first or last cell in column
+ const findBottomTopCell = (columnIndex: number, edge: 'b' | 't') => {
+ const row =
+ edge === 't'
+ ? tableElement?.querySelector('tr')
+ : tableElement?.lastElementChild?.lastElementChild;
+ const rowCells = Array.from(row?.children || []);
+ const targetCell = rowCells[columnIndex];
+ return targetCell as HTMLElement;
+ };
+
const findAdjacentCell = (
columnIndex: number,
searchDirection: 'f' | 'b',
@@ -173,10 +213,16 @@ export const cellNavigation = ({
nextCell = findAdjacentCell(currentIndex, 'f');
break;
case 'Home':
- nextCell = findEdgeCell(event.ctrlKey ? 'f' : 'c', 'f');
+ nextCell = findEdgeCell(isWinCtrlMacMeta(event) ? 'f' : 'c', 'f');
break;
case 'End':
- nextCell = findEdgeCell(event.ctrlKey ? 'l' : 'c', 'l');
+ nextCell = findEdgeCell(isWinCtrlMacMeta(event) ? 'l' : 'c', 'l');
+ break;
+ case 'PageUp':
+ nextCell = findBottomTopCell(currentIndex, 't');
+ break;
+ case 'PageDown':
+ nextCell = findBottomTopCell(currentIndex, 'b');
break;
}