@mini_7/material-react-table-mcp 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +1 -1
  2. package/data/examples/advanced.js +215 -0
  3. package/data/examples/aggregation-and-grouping.js +116 -0
  4. package/data/examples/aggregation-multi.js +89 -0
  5. package/data/examples/alternate-column-filtering.js +38 -0
  6. package/data/examples/alternate-detail-panel.js +65 -0
  7. package/data/examples/alternate-pagination.js +17 -0
  8. package/data/examples/basic.js +86 -0
  9. package/data/examples/chart-detail-panel.js +69 -0
  10. package/data/examples/column-actions-space.js +34 -0
  11. package/data/examples/column-alignment.js +58 -0
  12. package/data/examples/custom-column-actions.js +74 -0
  13. package/data/examples/custom-column-filtering-ui.js +59 -0
  14. package/data/examples/custom-headless.js +85 -0
  15. package/data/examples/custom-top-toolbar.js +60 -0
  16. package/data/examples/customize-display-columns.js +72 -0
  17. package/data/examples/customize-filter-components.js +78 -0
  18. package/data/examples/customize-filter-modes.js +59 -0
  19. package/data/examples/customize-filter-variants.js +98 -0
  20. package/data/examples/customize-global-filter-component.js +37 -0
  21. package/data/examples/customize-remove-column-grouping.js +75 -0
  22. package/data/examples/customize-row-selection.js +43 -0
  23. package/data/examples/customize-table-styles.js +81 -0
  24. package/data/examples/disable-column-actions.js +26 -0
  25. package/data/examples/disable-column-hiding.js +74 -0
  26. package/data/examples/disable-density-toggle.js +37 -0
  27. package/data/examples/dynamic-columns.js +124 -0
  28. package/data/examples/editing-crud-cell.js +288 -0
  29. package/data/examples/editing-crud-modal.js +275 -0
  30. package/data/examples/editing-crud-row.js +255 -0
  31. package/data/examples/editing-crud-table.js +280 -0
  32. package/data/examples/editing-crud-tree.js +328 -0
  33. package/data/examples/enable-cell-actions.js +52 -0
  34. package/data/examples/enable-click-to-copy.js +33 -0
  35. package/data/examples/enable-column-grouping.js +69 -0
  36. package/data/examples/enable-column-ordering.js +37 -0
  37. package/data/examples/enable-column-pinning.js +58 -0
  38. package/data/examples/enable-column-resizing.js +45 -0
  39. package/data/examples/enable-column-virtualization.js +19 -0
  40. package/data/examples/enable-detail-panel-conditionally.js +61 -0
  41. package/data/examples/enable-detail-panel-virtualized.js +64 -0
  42. package/data/examples/enable-expanding-tree.js +87 -0
  43. package/data/examples/enable-filter-facet-values.js +52 -0
  44. package/data/examples/enable-row-dragging.js +102 -0
  45. package/data/examples/enable-row-numbers-original.js +37 -0
  46. package/data/examples/enable-row-numbers-static.js +37 -0
  47. package/data/examples/enable-row-ordering.js +40 -0
  48. package/data/examples/enable-row-pinning-select.js +64 -0
  49. package/data/examples/enable-row-pinning-static.js +39 -0
  50. package/data/examples/enable-row-pinning-sticky.js +57 -0
  51. package/data/examples/enable-row-selection.js +71 -0
  52. package/data/examples/enable-row-virtualization.js +85 -0
  53. package/data/examples/enable-sticky-header.js +47 -0
  54. package/data/examples/expanding-tree-expanded.js +98 -0
  55. package/data/examples/expanding-tree-flat-parse.js +102 -0
  56. package/data/examples/expanding-tree-root-expanded.js +120 -0
  57. package/data/examples/export-to-csv.js +84 -0
  58. package/data/examples/export-to-pdf.js +76 -0
  59. package/data/examples/external-toolbar.js +82 -0
  60. package/data/examples/font-awesome-icons.js +62 -0
  61. package/data/examples/infinite-scrolling.js +137 -0
  62. package/data/examples/lazy-detail-panel.js +164 -0
  63. package/data/examples/lazy-sub-rows.js +118 -0
  64. package/data/examples/linear-progress.js +48 -0
  65. package/data/examples/loading.js +34 -0
  66. package/data/examples/localization-i18n-ar.js +40 -0
  67. package/data/examples/localization-i18n-az.js +38 -0
  68. package/data/examples/localization-i18n-bg.js +38 -0
  69. package/data/examples/localization-i18n-cs.js +38 -0
  70. package/data/examples/localization-i18n-da.js +38 -0
  71. package/data/examples/localization-i18n-de.js +38 -0
  72. package/data/examples/localization-i18n-el.js +38 -0
  73. package/data/examples/localization-i18n-en.js +38 -0
  74. package/data/examples/localization-i18n-es.js +38 -0
  75. package/data/examples/localization-i18n-et.js +38 -0
  76. package/data/examples/localization-i18n-fa.js +40 -0
  77. package/data/examples/localization-i18n-fi.js +38 -0
  78. package/data/examples/localization-i18n-fr.js +38 -0
  79. package/data/examples/localization-i18n-he.js +40 -0
  80. package/data/examples/localization-i18n-hr.js +38 -0
  81. package/data/examples/localization-i18n-hu.js +38 -0
  82. package/data/examples/localization-i18n-hy.js +40 -0
  83. package/data/examples/localization-i18n-id.js +38 -0
  84. package/data/examples/localization-i18n-it.js +38 -0
  85. package/data/examples/localization-i18n-ja.js +38 -0
  86. package/data/examples/localization-i18n-ko.js +38 -0
  87. package/data/examples/localization-i18n-nl.js +38 -0
  88. package/data/examples/localization-i18n-no.js +38 -0
  89. package/data/examples/localization-i18n-np.js +38 -0
  90. package/data/examples/localization-i18n-pl.js +38 -0
  91. package/data/examples/localization-i18n-pt-BR.js +38 -0
  92. package/data/examples/localization-i18n-pt.js +38 -0
  93. package/data/examples/localization-i18n-ro.js +38 -0
  94. package/data/examples/localization-i18n-ru.js +38 -0
  95. package/data/examples/localization-i18n-sk.js +38 -0
  96. package/data/examples/localization-i18n-sr-Cyrl-RS.js +38 -0
  97. package/data/examples/localization-i18n-sr-Latn-RS.js +38 -0
  98. package/data/examples/localization-i18n-sv.js +38 -0
  99. package/data/examples/localization-i18n-tr.js +38 -0
  100. package/data/examples/localization-i18n-uk.js +38 -0
  101. package/data/examples/localization-i18n-vi.js +38 -0
  102. package/data/examples/localization-i18n-zh-hans.js +38 -0
  103. package/data/examples/localization-i18n-zh-hant.js +38 -0
  104. package/data/examples/manual-selection.js +81 -0
  105. package/data/examples/minimal.js +66 -0
  106. package/data/examples/mui-theme.js +109 -0
  107. package/data/examples/multi-sorting.js +48 -0
  108. package/data/examples/persistent-state.js +126 -0
  109. package/data/examples/react-query.js +122 -0
  110. package/data/examples/remote.js +113 -0
  111. package/data/examples/row-actions-buttons.js +54 -0
  112. package/data/examples/row-actions-menu-items.js +36 -0
  113. package/data/examples/single-row-selection.js +74 -0
  114. package/data/examples/virtualized.js +122 -0
  115. package/dist/data.js +31 -9
  116. package/dist/index.js +18 -6
  117. package/package.json +1 -1
@@ -0,0 +1,122 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { IconButton, Tooltip } from '@mui/material';
4
+ import RefreshIcon from '@mui/icons-material/Refresh';
5
+ import { QueryClient, QueryClientProvider, keepPreviousData, useQuery, } from '@tanstack/react-query'; //note: this is TanStack React Query V5
6
+ const Example = () => {
7
+ //manage our own state for stuff we want to pass to the API
8
+ const [columnFilters, setColumnFilters] = useState([]);
9
+ const [globalFilter, setGlobalFilter] = useState('');
10
+ const [sorting, setSorting] = useState([]);
11
+ const [pagination, setPagination] = useState({
12
+ pageIndex: 0,
13
+ pageSize: 10,
14
+ });
15
+ //consider storing this code in a custom hook (i.e useFetchUsers)
16
+ const { data: { data = [], meta } = {}, //your data and api response will probably be different
17
+ isError, isRefetching, isLoading, refetch, } = useQuery({
18
+ queryKey: [
19
+ 'users-list',
20
+ {
21
+ columnFilters, //refetch when columnFilters changes
22
+ globalFilter, //refetch when globalFilter changes
23
+ pagination, //refetch when pagination changes
24
+ sorting, //refetch when sorting changes
25
+ },
26
+ ],
27
+ queryFn: async () => {
28
+ const fetchURL = new URL('/api/data', location.origin);
29
+ //read our state and pass it to the API as query params
30
+ fetchURL.searchParams.set('start', `${pagination.pageIndex * pagination.pageSize}`);
31
+ fetchURL.searchParams.set('size', `${pagination.pageSize}`);
32
+ fetchURL.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
33
+ fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
34
+ fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
35
+ //use whatever fetch library you want, fetch, axios, etc
36
+ const response = await fetch(fetchURL.href);
37
+ const json = (await response.json());
38
+ return json;
39
+ },
40
+ placeholderData: keepPreviousData, //don't go to 0 rows when refetching or paginating to next page
41
+ });
42
+ const columns = useMemo(
43
+ //column definitions...
44
+ () => [
45
+ {
46
+ accessorKey: 'firstName',
47
+ header: 'First Name',
48
+ },
49
+ {
50
+ accessorKey: 'lastName',
51
+ header: 'Last Name',
52
+ },
53
+ {
54
+ accessorKey: 'address',
55
+ header: 'Address',
56
+ },
57
+ {
58
+ accessorKey: 'state',
59
+ header: 'State',
60
+ },
61
+ {
62
+ accessorKey: 'phoneNumber',
63
+ header: 'Phone Number',
64
+ },
65
+ {
66
+ accessorFn: (row) => new Date(row.lastLogin),
67
+ id: 'lastLogin',
68
+ header: 'Last Login',
69
+ Cell: ({ cell }) => new Date(cell.getValue()).toLocaleString(),
70
+ filterFn: 'greaterThan',
71
+ filterVariant: 'date',
72
+ enableGlobalFilter: false,
73
+ },
74
+ ], []);
75
+ const table = useMaterialReactTable({
76
+ columns,
77
+ data,
78
+ initialState: { showColumnFilters: true },
79
+ manualFiltering: true, //turn off built-in client-side filtering
80
+ manualPagination: true, //turn off built-in client-side pagination
81
+ manualSorting: true, //turn off built-in client-side sorting
82
+ muiToolbarAlertBannerProps: isError
83
+ ? {
84
+ color: 'error',
85
+ children: 'Error loading data',
86
+ }
87
+ : undefined,
88
+ onColumnFiltersChange: setColumnFilters,
89
+ onGlobalFilterChange: setGlobalFilter,
90
+ onPaginationChange: setPagination,
91
+ onSortingChange: setSorting,
92
+ renderTopToolbarCustomActions: () => (<Tooltip arrow title="Refresh Data">
93
+ <IconButton onClick={() => refetch()}>
94
+ <RefreshIcon />
95
+ </IconButton>
96
+ </Tooltip>),
97
+ rowCount: meta?.totalRowCount ?? 0,
98
+ state: {
99
+ columnFilters,
100
+ globalFilter,
101
+ isLoading,
102
+ pagination,
103
+ showAlertBanner: isError,
104
+ showProgressBars: isRefetching,
105
+ sorting,
106
+ },
107
+ });
108
+ return <MaterialReactTable table={table}/>;
109
+ };
110
+ //react query setup in App.tsx
111
+ const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
112
+ default: d.ReactQueryDevtools,
113
+ })));
114
+ const queryClient = new QueryClient();
115
+ export default function App() {
116
+ return (<QueryClientProvider client={queryClient}>
117
+ <Example />
118
+ <Suspense fallback={null}>
119
+ <ReactQueryDevtoolsProduction />
120
+ </Suspense>
121
+ </QueryClientProvider>);
122
+ }
@@ -0,0 +1,113 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ const Example = () => {
4
+ //data and fetching state
5
+ const [data, setData] = useState([]);
6
+ const [isError, setIsError] = useState(false);
7
+ const [isLoading, setIsLoading] = useState(false);
8
+ const [isRefetching, setIsRefetching] = useState(false);
9
+ const [rowCount, setRowCount] = useState(0);
10
+ //table state
11
+ const [columnFilters, setColumnFilters] = useState([]);
12
+ const [globalFilter, setGlobalFilter] = useState('');
13
+ const [sorting, setSorting] = useState([]);
14
+ const [pagination, setPagination] = useState({
15
+ pageIndex: 0,
16
+ pageSize: 10,
17
+ });
18
+ //if you want to avoid useEffect, look at the React Query example instead
19
+ useEffect(() => {
20
+ const fetchData = async () => {
21
+ if (!data.length) {
22
+ setIsLoading(true);
23
+ }
24
+ else {
25
+ setIsRefetching(true);
26
+ }
27
+ const url = new URL('/api/data', location.origin);
28
+ url.searchParams.set('start', `${pagination.pageIndex * pagination.pageSize}`);
29
+ url.searchParams.set('size', `${pagination.pageSize}`);
30
+ url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
31
+ url.searchParams.set('globalFilter', globalFilter ?? '');
32
+ url.searchParams.set('sorting', JSON.stringify(sorting ?? []));
33
+ try {
34
+ const response = await fetch(url.href);
35
+ const json = (await response.json());
36
+ setData(json.data);
37
+ setRowCount(json.meta.totalRowCount);
38
+ }
39
+ catch (error) {
40
+ setIsError(true);
41
+ console.error(error);
42
+ return;
43
+ }
44
+ setIsError(false);
45
+ setIsLoading(false);
46
+ setIsRefetching(false);
47
+ };
48
+ fetchData();
49
+ // eslint-disable-next-line react-hooks/exhaustive-deps
50
+ }, [
51
+ columnFilters, //re-fetch when column filters change
52
+ globalFilter, //re-fetch when global filter changes
53
+ pagination.pageIndex, //re-fetch when page index changes
54
+ pagination.pageSize, //re-fetch when page size changes
55
+ sorting, //re-fetch when sorting changes
56
+ ]);
57
+ const columns = useMemo(() => [
58
+ {
59
+ accessorKey: 'firstName',
60
+ header: 'First Name',
61
+ },
62
+ //column definitions...
63
+ {
64
+ accessorKey: 'lastName',
65
+ header: 'Last Name',
66
+ },
67
+ {
68
+ accessorKey: 'address',
69
+ header: 'Address',
70
+ },
71
+ {
72
+ accessorKey: 'state',
73
+ header: 'State',
74
+ },
75
+ {
76
+ accessorKey: 'phoneNumber',
77
+ header: 'Phone Number',
78
+ },
79
+ //end
80
+ ], []);
81
+ const table = useMaterialReactTable({
82
+ columns,
83
+ data,
84
+ enableRowSelection: true,
85
+ getRowId: (row) => row.phoneNumber,
86
+ initialState: { showColumnFilters: true },
87
+ manualFiltering: true,
88
+ manualPagination: true,
89
+ manualSorting: true,
90
+ muiToolbarAlertBannerProps: isError
91
+ ? {
92
+ color: 'error',
93
+ children: 'Error loading data',
94
+ }
95
+ : undefined,
96
+ onColumnFiltersChange: setColumnFilters,
97
+ onGlobalFilterChange: setGlobalFilter,
98
+ onPaginationChange: setPagination,
99
+ onSortingChange: setSorting,
100
+ rowCount,
101
+ state: {
102
+ columnFilters,
103
+ globalFilter,
104
+ isLoading,
105
+ pagination,
106
+ showAlertBanner: isError,
107
+ showProgressBars: isRefetching,
108
+ sorting,
109
+ },
110
+ });
111
+ return <MaterialReactTable table={table}/>;
112
+ };
113
+ export default Example;
@@ -0,0 +1,54 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { MaterialReactTable } from 'material-react-table';
3
+ import { Box, IconButton } from '@mui/material';
4
+ import { Edit as EditIcon, Delete as DeleteIcon, Email as EmailIcon, } from '@mui/icons-material';
5
+ import { data as initialData } from './makeData';
6
+ export const Example = () => {
7
+ const columns = useMemo(
8
+ //column definitions...
9
+ () => [
10
+ {
11
+ accessorKey: 'firstName',
12
+ header: 'First Name',
13
+ },
14
+ {
15
+ accessorKey: 'lastName',
16
+ header: 'Last Name',
17
+ },
18
+ {
19
+ accessorKey: 'address',
20
+ header: 'Address',
21
+ },
22
+ {
23
+ accessorKey: 'city',
24
+ header: 'City',
25
+ },
26
+ {
27
+ accessorKey: 'state',
28
+ header: 'State',
29
+ },
30
+ ], []);
31
+ const [data, setData] = useState(initialData);
32
+ return (<MaterialReactTable columns={columns} data={data} layoutMode="grid" displayColumnDefOptions={{
33
+ 'mrt-row-actions': {
34
+ size: 180, //if using layoutMode that is not 'semantic', the columns will not auto-size, so you need to set the size manually
35
+ grow: false,
36
+ },
37
+ }} enableRowActions renderRowActions={({ row, table }) => (<Box sx={{ display: 'flex', flexWrap: 'nowrap', gap: '8px' }}>
38
+ <IconButton color="primary" onClick={() => window.open(`mailto:kevinvandy@mailinator.com?subject=Hello ${row.original.firstName}!`)}>
39
+ <EmailIcon />
40
+ </IconButton>
41
+ <IconButton color="secondary" onClick={() => {
42
+ table.setEditingRow(row);
43
+ }}>
44
+ <EditIcon />
45
+ </IconButton>
46
+ <IconButton color="error" onClick={() => {
47
+ data.splice(row.index, 1); //assuming simple data table
48
+ setData([...data]);
49
+ }}>
50
+ <DeleteIcon />
51
+ </IconButton>
52
+ </Box>)}/>);
53
+ };
54
+ export default Example;
@@ -0,0 +1,36 @@
1
+ import { useMemo } from 'react';
2
+ import { MaterialReactTable, MRT_ActionMenuItem, } from 'material-react-table';
3
+ import { data } from './makeData';
4
+ import { Edit, Delete } from '@mui/icons-material';
5
+ export const Example = () => {
6
+ const columns = useMemo(
7
+ //column definitions...
8
+ () => [
9
+ {
10
+ accessorKey: 'firstName',
11
+ header: 'First Name',
12
+ },
13
+ {
14
+ accessorKey: 'lastName',
15
+ header: 'Last Name',
16
+ },
17
+ {
18
+ accessorKey: 'address',
19
+ header: 'Address',
20
+ },
21
+ {
22
+ accessorKey: 'city',
23
+ header: 'City',
24
+ },
25
+ {
26
+ accessorKey: 'state',
27
+ header: 'State',
28
+ },
29
+ ], []);
30
+ return (<MaterialReactTable columns={columns} data={data} enableRowActions renderRowActionMenuItems={({ row, table }) => [
31
+ <MRT_ActionMenuItem //or just use a normal MUI MenuItem component
32
+ icon={<Edit />} key="edit" label="Edit" onClick={() => console.info('Edit')} table={table}/>,
33
+ <MRT_ActionMenuItem icon={<Delete />} key="delete" label="Delete" onClick={() => console.info('Delete')} table={table}/>,
34
+ ]}/>);
35
+ };
36
+ export default Example;
@@ -0,0 +1,74 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ //end
4
+ const data = [
5
+ //data definitions...
6
+ {
7
+ userId: '3f25309c-8fa1-470f-811e-cdb082ab9017', //we'll use this as a unique row id
8
+ firstName: 'Dylan',
9
+ lastName: 'Murray',
10
+ age: 22,
11
+ address: '261 Erdman Ford',
12
+ city: 'East Daphne',
13
+ state: 'Kentucky',
14
+ },
15
+ {
16
+ userId: 'be731030-df83-419c-b3d6-9ef04e7f4a9f',
17
+ firstName: 'Raquel',
18
+ lastName: 'Kohler',
19
+ age: 18,
20
+ address: '769 Dominic Grove',
21
+ city: 'Columbus',
22
+ state: 'Ohio',
23
+ },
24
+ //end
25
+ ];
26
+ const Example = () => {
27
+ const columns = useMemo(
28
+ //column definitions...
29
+ () => [
30
+ {
31
+ accessorKey: 'firstName',
32
+ header: 'First Name',
33
+ },
34
+ {
35
+ accessorKey: 'lastName',
36
+ header: 'Last Name',
37
+ },
38
+ {
39
+ accessorKey: 'age',
40
+ header: 'Age',
41
+ },
42
+ {
43
+ accessorKey: 'address',
44
+ header: 'Address',
45
+ },
46
+ {
47
+ accessorKey: 'city',
48
+ header: 'City',
49
+ },
50
+ {
51
+ accessorKey: 'state',
52
+ header: 'State',
53
+ },
54
+ ], []);
55
+ //optionally, you can manage the row selection state yourself
56
+ const [rowSelection, setRowSelection] = useState({});
57
+ const table = useMaterialReactTable({
58
+ columns,
59
+ data,
60
+ enableRowSelection: true,
61
+ enableMultiRowSelection: false, //use radio buttons instead of checkboxes
62
+ getRowId: (row) => row.userId, //give each row a more useful id
63
+ muiTableBodyRowProps: ({ row }) => ({
64
+ //add onClick to row to select upon clicking anywhere in the row
65
+ onClick: row.getToggleSelectedHandler(),
66
+ sx: { cursor: 'pointer' },
67
+ }),
68
+ positionToolbarAlertBanner: 'bottom', //move the alert banner to the bottom
69
+ onRowSelectionChange: setRowSelection, //connect internal row selection state to your own
70
+ state: { rowSelection }, //pass our managed row selection state to the table to use
71
+ });
72
+ return <MaterialReactTable table={table}/>;
73
+ };
74
+ export default Example;
@@ -0,0 +1,122 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { makeData } from './makeData';
4
+ const Example = () => {
5
+ const columns = useMemo(
6
+ //column definitions...
7
+ () => [
8
+ {
9
+ accessorKey: 'firstName',
10
+ header: 'First Name',
11
+ size: 150,
12
+ },
13
+ {
14
+ accessorKey: 'middleName',
15
+ header: 'Middle Name',
16
+ size: 170,
17
+ },
18
+ {
19
+ accessorKey: 'lastName',
20
+ header: 'Last Name',
21
+ size: 150,
22
+ },
23
+ {
24
+ accessorKey: 'email',
25
+ header: 'Email Address',
26
+ size: 300,
27
+ },
28
+ {
29
+ accessorKey: 'phoneNumber',
30
+ header: 'Phone Number',
31
+ size: 250,
32
+ },
33
+ {
34
+ accessorKey: 'address',
35
+ header: 'Address',
36
+ size: 300,
37
+ },
38
+ {
39
+ accessorKey: 'zipCode',
40
+ header: 'Zip Code',
41
+ },
42
+ {
43
+ accessorKey: 'city',
44
+ header: 'City',
45
+ size: 220,
46
+ },
47
+ {
48
+ accessorKey: 'state',
49
+ header: 'State',
50
+ },
51
+ {
52
+ accessorKey: 'country',
53
+ header: 'Country',
54
+ size: 350,
55
+ },
56
+ {
57
+ accessorKey: 'petName',
58
+ header: 'Pet Name',
59
+ },
60
+ {
61
+ accessorKey: 'age',
62
+ header: 'Age',
63
+ },
64
+ {
65
+ accessorKey: 'salary',
66
+ header: 'Salary',
67
+ },
68
+ {
69
+ accessorKey: 'dateOfBirth',
70
+ header: 'Date of Birth',
71
+ },
72
+ {
73
+ accessorKey: 'dateOfJoining',
74
+ header: 'Date of Joining',
75
+ },
76
+ {
77
+ accessorKey: 'isActive',
78
+ header: 'Is Active',
79
+ },
80
+ ], []);
81
+ //optionally access the underlying virtualizer instance
82
+ const rowVirtualizerInstanceRef = useRef(null);
83
+ const [data, setData] = useState([]);
84
+ const [isLoading, setIsLoading] = useState(true);
85
+ const [sorting, setSorting] = useState([]);
86
+ useEffect(() => {
87
+ if (typeof window !== 'undefined') {
88
+ setData(makeData(10000));
89
+ setIsLoading(false);
90
+ }
91
+ }, []);
92
+ useEffect(() => {
93
+ //scroll to the top of the table when the sorting changes
94
+ try {
95
+ rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
96
+ }
97
+ catch (error) {
98
+ console.error(error);
99
+ }
100
+ }, [sorting]);
101
+ const table = useMaterialReactTable({
102
+ columns,
103
+ data, //10,000 rows
104
+ defaultDisplayColumn: { enableResizing: true },
105
+ enableBottomToolbar: false,
106
+ enableColumnResizing: true,
107
+ enableColumnVirtualization: true,
108
+ enableGlobalFilterModes: true,
109
+ enablePagination: false,
110
+ enableColumnPinning: true,
111
+ enableRowNumbers: true,
112
+ enableRowVirtualization: true,
113
+ muiTableContainerProps: { sx: { maxHeight: '600px' } },
114
+ onSortingChange: setSorting,
115
+ state: { isLoading, sorting },
116
+ rowVirtualizerInstanceRef, //optional
117
+ rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
118
+ columnVirtualizerOptions: { overscan: 2 }, //optionally customize the column virtualizer
119
+ });
120
+ return <MaterialReactTable table={table}/>;
121
+ };
122
+ export default Example;
package/dist/data.js CHANGED
@@ -36,13 +36,28 @@ export const loadReference = (name) => {
36
36
  return undefined;
37
37
  return readFileSync(file, 'utf8');
38
38
  };
39
- export const loadExample = (id) => {
40
- const file = join(dataDir, 'examples', `${id}.tsx`);
39
+ export const loadExample = (id, language = 'ts') => {
40
+ const ext = language === 'js' ? 'js' : 'tsx';
41
+ const file = join(dataDir, 'examples', `${id}.${ext}`);
41
42
  if (!safeName.test(id) || !existsSync(file))
42
43
  return undefined;
43
44
  return readFileSync(file, 'utf8');
44
45
  };
45
46
  const tokenize = (query) => query.toLowerCase().split(/\s+/).filter(Boolean);
47
+ const STEM_SUFFIXES = ['ings', 'ing', 'ed', 'es', 's'];
48
+ const MIN_STEM_LENGTH = 3;
49
+ /**
50
+ * Strips a common English suffix so e.g. "filtering" and "filters" meet in the
51
+ * middle at "filter". Leaves words too short to keep a meaningful stem
52
+ * untouched (e.g. "is", "us").
53
+ */
54
+ const stem = (word) => {
55
+ for (const suffix of STEM_SUFFIXES) {
56
+ if (word.length - suffix.length >= MIN_STEM_LENGTH && word.endsWith(suffix))
57
+ return word.slice(0, -suffix.length);
58
+ }
59
+ return word;
60
+ };
46
61
  /** Ranks name matches above description matches; exact and prefix matches first. */
47
62
  export const searchApi = (api, query, categories = CATEGORIES, limit = 20) => {
48
63
  const terms = tokenize(query);
@@ -76,9 +91,12 @@ export const searchApi = (api, query, categories = CATEGORIES, limit = 20) => {
76
91
  .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
77
92
  .slice(0, limit);
78
93
  };
79
- /** Every term must match the name, title, headings, or description of a document. */
94
+ /**
95
+ * Every term must match the name, title, headings, or description of a document.
96
+ * Terms and indexed text are lightly stemmed so e.g. "filters" matches "filtering".
97
+ */
80
98
  export const searchDocs = (index, query, limit = 20, kinds = ['guide', 'reference', 'skill', 'example']) => {
81
- const terms = tokenize(query);
99
+ const terms = tokenize(query).map((term) => ({ term, termStem: stem(term) }));
82
100
  if (terms.length === 0)
83
101
  return [];
84
102
  const docs = [];
@@ -108,15 +126,19 @@ export const searchDocs = (index, query, limit = 20, kinds = ['guide', 'referenc
108
126
  if (!kinds.includes(doc.kind))
109
127
  continue;
110
128
  const name = doc.name.toLowerCase();
129
+ const nameParts = name.split('-');
130
+ const nameStem = stem(name);
131
+ const namePartStems = nameParts.map(stem);
132
+ const textStem = doc.text.split(/\s+/).map(stem).join(' ');
111
133
  let score = 0;
112
- for (const term of terms) {
113
- if (name === term)
134
+ for (const { term, termStem } of terms) {
135
+ if (name === term || nameStem === termStem)
114
136
  score += 100;
115
- else if (name.split('-').includes(term))
137
+ else if (nameParts.includes(term) || namePartStems.includes(termStem))
116
138
  score += 60;
117
- else if (name.includes(term))
139
+ else if (name.includes(term) || nameStem.includes(termStem))
118
140
  score += 40;
119
- else if (doc.text.includes(term))
141
+ else if (doc.text.includes(term) || textStem.includes(termStem))
120
142
  score += 10;
121
143
  else {
122
144
  score = 0;
package/dist/index.js CHANGED
@@ -201,14 +201,18 @@ server.registerTool('get_mrt_reference', {
201
201
  }, async ({ page }) => text(loadReference(page) ?? `Unknown page "${page}".`));
202
202
  server.registerTool('get_mrt_example', {
203
203
  title: 'Get a Material React Table example',
204
- description: `Return the full TypeScript source of one of the ${docs.examples.length} runnable docs examples. Call without an id to list every example with the guides that embed it.`,
204
+ description: `Return the source of one of the ${docs.examples.length} runnable docs examples, as TypeScript or JavaScript. Call without an id to list every example with the guides that embed it.`,
205
205
  inputSchema: {
206
206
  id: z
207
207
  .string()
208
208
  .optional()
209
209
  .describe('Example id, e.g. "basic", "editing-crud-modal", "virtualized", "remote". Omit to list all examples.'),
210
+ language: z
211
+ .enum(['ts', 'js'])
212
+ .optional()
213
+ .describe('Source language to return, "ts" or "js". Defaults to "ts".'),
210
214
  },
211
- }, async ({ id }) => {
215
+ }, async ({ id, language }) => {
212
216
  if (!id) {
213
217
  const lines = docs.examples.map((example) => {
214
218
  const where = [
@@ -221,12 +225,20 @@ server.registerTool('get_mrt_example', {
221
225
  });
222
226
  return text(`Examples:\n${lines.join('\n')}`);
223
227
  }
224
- const source = loadExample(id);
228
+ const lang = language ?? 'ts';
229
+ const source = loadExample(id, lang);
225
230
  if (!source) {
226
- const suggestions = searchDocs(docs, id, 5, ['example']).map((hit) => hit.name);
227
- return text(`Unknown example "${id}".${suggestions.length ? ` Did you mean: ${suggestions.join(', ')}?` : ''} Call get_mrt_example without an id to list them.`);
231
+ const known = docs.examples.some((example) => example.id === id);
232
+ if (!known) {
233
+ const suggestions = searchDocs(docs, id, 5, ['example']).map((hit) => hit.name);
234
+ return text(`Unknown example "${id}".${suggestions.length ? ` Did you mean: ${suggestions.join(', ')}?` : ''} Call get_mrt_example without an id to list them.`);
235
+ }
236
+ return text(`No ${lang} source is available for example "${id}".`);
228
237
  }
229
- return text(`// examples/${id}/sandbox/src/TS.tsx\n${source}`);
238
+ const sourceNote = lang === 'js'
239
+ ? `// examples/${id}/sandbox/src/TS.tsx (JavaScript, types stripped)`
240
+ : `// examples/${id}/sandbox/src/TS.tsx`;
241
+ return text(`${sourceNote}\n${source}`);
230
242
  });
231
243
  const transport = new StdioServerTransport();
232
244
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mini_7/material-react-table-mcp",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "MCP server that lets AI coding agents look up Material React Table V4 table options, column options, state, instance APIs, the V3 to V4 migration guide, and the bundled agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "Tyler Lee",