@lminii/material-react-table-mcp 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 (284) hide show
  1. package/README.md +68 -0
  2. package/data/MIGRATION.md +136 -0
  3. package/data/api.json +5379 -0
  4. package/data/examples/advanced.js +215 -0
  5. package/data/examples/advanced.tsx +309 -0
  6. package/data/examples/aggregation-and-grouping.js +116 -0
  7. package/data/examples/aggregation-and-grouping.tsx +153 -0
  8. package/data/examples/aggregation-multi.js +89 -0
  9. package/data/examples/aggregation-multi.tsx +105 -0
  10. package/data/examples/alternate-column-filtering.js +38 -0
  11. package/data/examples/alternate-column-filtering.tsx +49 -0
  12. package/data/examples/alternate-detail-panel.js +65 -0
  13. package/data/examples/alternate-detail-panel.tsx +81 -0
  14. package/data/examples/alternate-pagination.js +17 -0
  15. package/data/examples/alternate-pagination.tsx +23 -0
  16. package/data/examples/basic.js +86 -0
  17. package/data/examples/basic.tsx +109 -0
  18. package/data/examples/chart-detail-panel.js +69 -0
  19. package/data/examples/chart-detail-panel.tsx +89 -0
  20. package/data/examples/column-actions-space.js +34 -0
  21. package/data/examples/column-actions-space.tsx +44 -0
  22. package/data/examples/column-alignment.js +58 -0
  23. package/data/examples/column-alignment.tsx +70 -0
  24. package/data/examples/custom-column-actions.js +74 -0
  25. package/data/examples/custom-column-actions.tsx +109 -0
  26. package/data/examples/custom-column-filtering-ui.js +59 -0
  27. package/data/examples/custom-column-filtering-ui.tsx +89 -0
  28. package/data/examples/custom-headless.js +85 -0
  29. package/data/examples/custom-headless.tsx +125 -0
  30. package/data/examples/custom-top-toolbar.js +60 -0
  31. package/data/examples/custom-top-toolbar.tsx +88 -0
  32. package/data/examples/customize-display-columns.js +72 -0
  33. package/data/examples/customize-display-columns.tsx +85 -0
  34. package/data/examples/customize-filter-components.js +78 -0
  35. package/data/examples/customize-filter-components.tsx +101 -0
  36. package/data/examples/customize-filter-modes.js +59 -0
  37. package/data/examples/customize-filter-modes.tsx +73 -0
  38. package/data/examples/customize-filter-variants.js +98 -0
  39. package/data/examples/customize-filter-variants.tsx +116 -0
  40. package/data/examples/customize-global-filter-component.js +37 -0
  41. package/data/examples/customize-global-filter-component.tsx +52 -0
  42. package/data/examples/customize-remove-column-grouping.js +75 -0
  43. package/data/examples/customize-remove-column-grouping.tsx +94 -0
  44. package/data/examples/customize-row-selection.js +43 -0
  45. package/data/examples/customize-row-selection.tsx +54 -0
  46. package/data/examples/customize-table-styles.js +81 -0
  47. package/data/examples/customize-table-styles.tsx +100 -0
  48. package/data/examples/disable-column-actions.js +26 -0
  49. package/data/examples/disable-column-actions.tsx +37 -0
  50. package/data/examples/disable-column-hiding.js +74 -0
  51. package/data/examples/disable-column-hiding.tsx +90 -0
  52. package/data/examples/disable-density-toggle.js +37 -0
  53. package/data/examples/disable-density-toggle.tsx +49 -0
  54. package/data/examples/dynamic-columns.js +124 -0
  55. package/data/examples/dynamic-columns.tsx +189 -0
  56. package/data/examples/editing-crud-cell.js +288 -0
  57. package/data/examples/editing-crud-cell.tsx +377 -0
  58. package/data/examples/editing-crud-modal.js +275 -0
  59. package/data/examples/editing-crud-modal.tsx +370 -0
  60. package/data/examples/editing-crud-row.js +255 -0
  61. package/data/examples/editing-crud-row.tsx +333 -0
  62. package/data/examples/editing-crud-table.js +280 -0
  63. package/data/examples/editing-crud-table.tsx +369 -0
  64. package/data/examples/editing-crud-tree.js +328 -0
  65. package/data/examples/editing-crud-tree.tsx +416 -0
  66. package/data/examples/enable-cell-actions.js +52 -0
  67. package/data/examples/enable-cell-actions.tsx +76 -0
  68. package/data/examples/enable-click-to-copy.js +33 -0
  69. package/data/examples/enable-click-to-copy.tsx +39 -0
  70. package/data/examples/enable-column-grouping.js +69 -0
  71. package/data/examples/enable-column-grouping.tsx +120 -0
  72. package/data/examples/enable-column-ordering.js +37 -0
  73. package/data/examples/enable-column-ordering.tsx +48 -0
  74. package/data/examples/enable-column-pinning.js +58 -0
  75. package/data/examples/enable-column-pinning.tsx +69 -0
  76. package/data/examples/enable-column-resizing.js +45 -0
  77. package/data/examples/enable-column-resizing.tsx +56 -0
  78. package/data/examples/enable-column-virtualization.js +19 -0
  79. package/data/examples/enable-column-virtualization.tsx +27 -0
  80. package/data/examples/enable-detail-panel-conditionally.js +61 -0
  81. package/data/examples/enable-detail-panel-conditionally.tsx +78 -0
  82. package/data/examples/enable-detail-panel-virtualized.js +64 -0
  83. package/data/examples/enable-detail-panel-virtualized.tsx +80 -0
  84. package/data/examples/enable-expanding-tree.js +87 -0
  85. package/data/examples/enable-expanding-tree.tsx +110 -0
  86. package/data/examples/enable-filter-facet-values.js +52 -0
  87. package/data/examples/enable-filter-facet-values.tsx +65 -0
  88. package/data/examples/enable-row-dragging.js +102 -0
  89. package/data/examples/enable-row-dragging.tsx +137 -0
  90. package/data/examples/enable-row-numbers-original.js +37 -0
  91. package/data/examples/enable-row-numbers-original.tsx +48 -0
  92. package/data/examples/enable-row-numbers-static.js +37 -0
  93. package/data/examples/enable-row-numbers-static.tsx +48 -0
  94. package/data/examples/enable-row-ordering.js +40 -0
  95. package/data/examples/enable-row-ordering.tsx +57 -0
  96. package/data/examples/enable-row-pinning-select.js +64 -0
  97. package/data/examples/enable-row-pinning-select.tsx +76 -0
  98. package/data/examples/enable-row-pinning-static.js +39 -0
  99. package/data/examples/enable-row-pinning-static.tsx +50 -0
  100. package/data/examples/enable-row-pinning-sticky.js +57 -0
  101. package/data/examples/enable-row-pinning-sticky.tsx +69 -0
  102. package/data/examples/enable-row-selection.js +71 -0
  103. package/data/examples/enable-row-selection.tsx +96 -0
  104. package/data/examples/enable-row-virtualization.js +85 -0
  105. package/data/examples/enable-row-virtualization.tsx +101 -0
  106. package/data/examples/enable-sticky-header.js +47 -0
  107. package/data/examples/enable-sticky-header.tsx +59 -0
  108. package/data/examples/expanding-tree-expanded.js +98 -0
  109. package/data/examples/expanding-tree-expanded.tsx +121 -0
  110. package/data/examples/expanding-tree-flat-parse.js +102 -0
  111. package/data/examples/expanding-tree-flat-parse.tsx +125 -0
  112. package/data/examples/expanding-tree-root-expanded.js +120 -0
  113. package/data/examples/expanding-tree-root-expanded.tsx +151 -0
  114. package/data/examples/export-to-csv.js +84 -0
  115. package/data/examples/export-to-csv.tsx +114 -0
  116. package/data/examples/export-to-pdf.js +76 -0
  117. package/data/examples/export-to-pdf.tsx +104 -0
  118. package/data/examples/external-toolbar.js +82 -0
  119. package/data/examples/external-toolbar.tsx +108 -0
  120. package/data/examples/font-awesome-icons.js +62 -0
  121. package/data/examples/font-awesome-icons.tsx +106 -0
  122. package/data/examples/infinite-scrolling.js +137 -0
  123. package/data/examples/infinite-scrolling.tsx +208 -0
  124. package/data/examples/lazy-detail-panel.js +164 -0
  125. package/data/examples/lazy-detail-panel.tsx +262 -0
  126. package/data/examples/lazy-sub-rows.js +118 -0
  127. package/data/examples/lazy-sub-rows.tsx +190 -0
  128. package/data/examples/linear-progress.js +48 -0
  129. package/data/examples/linear-progress.tsx +66 -0
  130. package/data/examples/loading.js +34 -0
  131. package/data/examples/loading.tsx +50 -0
  132. package/data/examples/localization-i18n-ar.js +40 -0
  133. package/data/examples/localization-i18n-ar.tsx +65 -0
  134. package/data/examples/localization-i18n-az.js +38 -0
  135. package/data/examples/localization-i18n-az.tsx +60 -0
  136. package/data/examples/localization-i18n-bg.js +38 -0
  137. package/data/examples/localization-i18n-bg.tsx +60 -0
  138. package/data/examples/localization-i18n-cs.js +38 -0
  139. package/data/examples/localization-i18n-cs.tsx +60 -0
  140. package/data/examples/localization-i18n-da.js +38 -0
  141. package/data/examples/localization-i18n-da.tsx +60 -0
  142. package/data/examples/localization-i18n-de.js +38 -0
  143. package/data/examples/localization-i18n-de.tsx +60 -0
  144. package/data/examples/localization-i18n-el.js +38 -0
  145. package/data/examples/localization-i18n-el.tsx +60 -0
  146. package/data/examples/localization-i18n-en.js +38 -0
  147. package/data/examples/localization-i18n-en.tsx +60 -0
  148. package/data/examples/localization-i18n-es.js +38 -0
  149. package/data/examples/localization-i18n-es.tsx +60 -0
  150. package/data/examples/localization-i18n-et.js +38 -0
  151. package/data/examples/localization-i18n-et.tsx +60 -0
  152. package/data/examples/localization-i18n-fa.js +40 -0
  153. package/data/examples/localization-i18n-fa.tsx +66 -0
  154. package/data/examples/localization-i18n-fi.js +38 -0
  155. package/data/examples/localization-i18n-fi.tsx +60 -0
  156. package/data/examples/localization-i18n-fr.js +38 -0
  157. package/data/examples/localization-i18n-fr.tsx +60 -0
  158. package/data/examples/localization-i18n-he.js +40 -0
  159. package/data/examples/localization-i18n-he.tsx +66 -0
  160. package/data/examples/localization-i18n-hr.js +38 -0
  161. package/data/examples/localization-i18n-hr.tsx +60 -0
  162. package/data/examples/localization-i18n-hu.js +38 -0
  163. package/data/examples/localization-i18n-hu.tsx +60 -0
  164. package/data/examples/localization-i18n-hy.js +40 -0
  165. package/data/examples/localization-i18n-hy.tsx +65 -0
  166. package/data/examples/localization-i18n-id.js +38 -0
  167. package/data/examples/localization-i18n-id.tsx +60 -0
  168. package/data/examples/localization-i18n-it.js +38 -0
  169. package/data/examples/localization-i18n-it.tsx +60 -0
  170. package/data/examples/localization-i18n-ja.js +38 -0
  171. package/data/examples/localization-i18n-ja.tsx +60 -0
  172. package/data/examples/localization-i18n-ko.js +38 -0
  173. package/data/examples/localization-i18n-ko.tsx +60 -0
  174. package/data/examples/localization-i18n-nl.js +38 -0
  175. package/data/examples/localization-i18n-nl.tsx +60 -0
  176. package/data/examples/localization-i18n-no.js +38 -0
  177. package/data/examples/localization-i18n-no.tsx +60 -0
  178. package/data/examples/localization-i18n-np.js +38 -0
  179. package/data/examples/localization-i18n-np.tsx +60 -0
  180. package/data/examples/localization-i18n-pl.js +38 -0
  181. package/data/examples/localization-i18n-pl.tsx +60 -0
  182. package/data/examples/localization-i18n-pt-BR.js +38 -0
  183. package/data/examples/localization-i18n-pt-BR.tsx +60 -0
  184. package/data/examples/localization-i18n-pt.js +38 -0
  185. package/data/examples/localization-i18n-pt.tsx +60 -0
  186. package/data/examples/localization-i18n-ro.js +38 -0
  187. package/data/examples/localization-i18n-ro.tsx +60 -0
  188. package/data/examples/localization-i18n-ru.js +38 -0
  189. package/data/examples/localization-i18n-ru.tsx +60 -0
  190. package/data/examples/localization-i18n-sk.js +38 -0
  191. package/data/examples/localization-i18n-sk.tsx +60 -0
  192. package/data/examples/localization-i18n-sr-Cyrl-RS.js +38 -0
  193. package/data/examples/localization-i18n-sr-Cyrl-RS.tsx +60 -0
  194. package/data/examples/localization-i18n-sr-Latn-RS.js +38 -0
  195. package/data/examples/localization-i18n-sr-Latn-RS.tsx +60 -0
  196. package/data/examples/localization-i18n-sv.js +38 -0
  197. package/data/examples/localization-i18n-sv.tsx +60 -0
  198. package/data/examples/localization-i18n-tr.js +38 -0
  199. package/data/examples/localization-i18n-tr.tsx +60 -0
  200. package/data/examples/localization-i18n-uk.js +38 -0
  201. package/data/examples/localization-i18n-uk.tsx +60 -0
  202. package/data/examples/localization-i18n-vi.js +38 -0
  203. package/data/examples/localization-i18n-vi.tsx +60 -0
  204. package/data/examples/localization-i18n-zh-hans.js +38 -0
  205. package/data/examples/localization-i18n-zh-hans.tsx +60 -0
  206. package/data/examples/localization-i18n-zh-hant.js +38 -0
  207. package/data/examples/localization-i18n-zh-hant.tsx +60 -0
  208. package/data/examples/manual-selection.js +81 -0
  209. package/data/examples/manual-selection.tsx +105 -0
  210. package/data/examples/minimal.js +66 -0
  211. package/data/examples/minimal.tsx +78 -0
  212. package/data/examples/mui-theme.js +109 -0
  213. package/data/examples/mui-theme.tsx +136 -0
  214. package/data/examples/multi-sorting.js +48 -0
  215. package/data/examples/multi-sorting.tsx +58 -0
  216. package/data/examples/persistent-state.js +126 -0
  217. package/data/examples/persistent-state.tsx +184 -0
  218. package/data/examples/react-query.js +122 -0
  219. package/data/examples/react-query.tsx +182 -0
  220. package/data/examples/remote.js +113 -0
  221. package/data/examples/remote.tsx +150 -0
  222. package/data/examples/row-actions-buttons.js +54 -0
  223. package/data/examples/row-actions-buttons.tsx +89 -0
  224. package/data/examples/row-actions-menu-items.js +36 -0
  225. package/data/examples/row-actions-menu-items.tsx +64 -0
  226. package/data/examples/single-row-selection.js +74 -0
  227. package/data/examples/single-row-selection.tsx +98 -0
  228. package/data/examples/virtualized.js +122 -0
  229. package/data/examples/virtualized.tsx +138 -0
  230. package/data/guides/accessibility.md +119 -0
  231. package/data/guides/aggregation.md +210 -0
  232. package/data/guides/async-loading.md +132 -0
  233. package/data/guides/best-practices.md +363 -0
  234. package/data/guides/cell-actions.md +139 -0
  235. package/data/guides/click-to-copy.md +67 -0
  236. package/data/guides/column-actions.md +69 -0
  237. package/data/guides/column-filtering.md +366 -0
  238. package/data/guides/column-grouping.md +192 -0
  239. package/data/guides/column-hiding.md +133 -0
  240. package/data/guides/column-ordering-dnd.md +99 -0
  241. package/data/guides/column-pinning.md +72 -0
  242. package/data/guides/column-resizing.md +157 -0
  243. package/data/guides/column-size.md +162 -0
  244. package/data/guides/customize-components.md +395 -0
  245. package/data/guides/customize-icons.md +35 -0
  246. package/data/guides/data-columns.md +292 -0
  247. package/data/guides/density-toggle.md +46 -0
  248. package/data/guides/detail-panel.md +195 -0
  249. package/data/guides/display-columns.md +135 -0
  250. package/data/guides/editing.md +367 -0
  251. package/data/guides/expanding-sub-rows.md +194 -0
  252. package/data/guides/full-screen-toggle.md +63 -0
  253. package/data/guides/global-filtering.md +237 -0
  254. package/data/guides/localization.md +57 -0
  255. package/data/guides/memoization.md +157 -0
  256. package/data/guides/pagination.md +171 -0
  257. package/data/guides/row-actions.md +116 -0
  258. package/data/guides/row-numbers.md +24 -0
  259. package/data/guides/row-ordering-dnd.md +51 -0
  260. package/data/guides/row-pinning.md +96 -0
  261. package/data/guides/row-selection.md +237 -0
  262. package/data/guides/sorting.md +190 -0
  263. package/data/guides/state-management.md +165 -0
  264. package/data/guides/sticky-header.md +41 -0
  265. package/data/guides/table-event-listeners.md +103 -0
  266. package/data/guides/toolbar-customization.md +251 -0
  267. package/data/guides/virtualization.md +183 -0
  268. package/data/index.json +1930 -0
  269. package/data/reference/mrt-components.md +183 -0
  270. package/data/reference/mrt-hooks.md +125 -0
  271. package/data/skills/README.md +81 -0
  272. package/data/skills/composable-components/SKILL.md +189 -0
  273. package/data/skills/customization/SKILL.md +190 -0
  274. package/data/skills/drag-and-drop-ordering/SKILL.md +227 -0
  275. package/data/skills/editing/SKILL.md +243 -0
  276. package/data/skills/filtering/SKILL.md +213 -0
  277. package/data/skills/getting-started/SKILL.md +213 -0
  278. package/data/skills/localization/SKILL.md +182 -0
  279. package/data/skills/migrate-v3-to-v4/SKILL.md +161 -0
  280. package/data/skills/state-and-server-data/SKILL.md +200 -0
  281. package/data/skills/virtualization/SKILL.md +203 -0
  282. package/dist/data.js +159 -0
  283. package/dist/index.js +244 -0
  284. package/package.json +50 -0
@@ -0,0 +1,275 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import { MRT_EditActionButtons, MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { Box, Button, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip, } from '@mui/material';
4
+ import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
5
+ import { fakeData, usStates } from './makeData';
6
+ import EditIcon from '@mui/icons-material/Edit';
7
+ import DeleteIcon from '@mui/icons-material/Delete';
8
+ const Example = () => {
9
+ const [validationErrors, setValidationErrors] = useState({});
10
+ const columns = useMemo(() => [
11
+ {
12
+ accessorKey: 'id',
13
+ header: 'Id',
14
+ enableEditing: false,
15
+ size: 80,
16
+ },
17
+ {
18
+ accessorKey: 'firstName',
19
+ header: 'First Name',
20
+ muiEditTextFieldProps: {
21
+ required: true,
22
+ error: !!validationErrors?.firstName,
23
+ helperText: validationErrors?.firstName,
24
+ //remove any previous validation errors when user focuses on the input
25
+ onFocus: () => setValidationErrors({
26
+ ...validationErrors,
27
+ firstName: undefined,
28
+ }),
29
+ //optionally add validation checking for onBlur or onChange
30
+ },
31
+ },
32
+ {
33
+ accessorKey: 'lastName',
34
+ header: 'Last Name',
35
+ muiEditTextFieldProps: {
36
+ required: true,
37
+ error: !!validationErrors?.lastName,
38
+ helperText: validationErrors?.lastName,
39
+ //remove any previous validation errors when user focuses on the input
40
+ onFocus: () => setValidationErrors({
41
+ ...validationErrors,
42
+ lastName: undefined,
43
+ }),
44
+ },
45
+ },
46
+ {
47
+ accessorKey: 'email',
48
+ header: 'Email',
49
+ muiEditTextFieldProps: {
50
+ type: 'email',
51
+ required: true,
52
+ error: !!validationErrors?.email,
53
+ helperText: validationErrors?.email,
54
+ //remove any previous validation errors when user focuses on the input
55
+ onFocus: () => setValidationErrors({
56
+ ...validationErrors,
57
+ email: undefined,
58
+ }),
59
+ },
60
+ },
61
+ {
62
+ accessorKey: 'state',
63
+ header: 'State',
64
+ editVariant: 'select',
65
+ editSelectOptions: usStates,
66
+ muiEditTextFieldProps: {
67
+ select: true,
68
+ error: !!validationErrors?.state,
69
+ helperText: validationErrors?.state,
70
+ },
71
+ },
72
+ ], [validationErrors]);
73
+ //call CREATE hook
74
+ const { mutateAsync: createUser, isPending: isCreatingUser } = useCreateUser();
75
+ //call READ hook
76
+ const { data: fetchedUsers = [], isError: isLoadingUsersError, isFetching: isFetchingUsers, isLoading: isLoadingUsers, } = useGetUsers();
77
+ //call UPDATE hook
78
+ const { mutateAsync: updateUser, isPending: isUpdatingUser } = useUpdateUser();
79
+ //call DELETE hook
80
+ const { mutateAsync: deleteUser, isPending: isDeletingUser } = useDeleteUser();
81
+ //CREATE action
82
+ const handleCreateUser = async ({ values, table, }) => {
83
+ const newValidationErrors = validateUser(values);
84
+ if (Object.values(newValidationErrors).some((error) => error)) {
85
+ setValidationErrors(newValidationErrors);
86
+ return;
87
+ }
88
+ setValidationErrors({});
89
+ await createUser(values);
90
+ table.setCreatingRow(null); //exit creating mode
91
+ };
92
+ //UPDATE action
93
+ const handleSaveUser = async ({ values, table, }) => {
94
+ const newValidationErrors = validateUser(values);
95
+ if (Object.values(newValidationErrors).some((error) => error)) {
96
+ setValidationErrors(newValidationErrors);
97
+ return;
98
+ }
99
+ setValidationErrors({});
100
+ await updateUser(values);
101
+ table.setEditingRow(null); //exit editing mode
102
+ };
103
+ //DELETE action
104
+ const openDeleteConfirmModal = (row) => {
105
+ if (window.confirm('Are you sure you want to delete this user?')) {
106
+ deleteUser(row.original.id);
107
+ }
108
+ };
109
+ const table = useMaterialReactTable({
110
+ columns,
111
+ data: fetchedUsers,
112
+ createDisplayMode: 'modal', //default ('row', and 'custom' are also available)
113
+ editDisplayMode: 'modal', //default ('row', 'cell', 'table', and 'custom' are also available)
114
+ enableEditing: true,
115
+ getRowId: (row) => row.id,
116
+ muiToolbarAlertBannerProps: isLoadingUsersError
117
+ ? {
118
+ color: 'error',
119
+ children: 'Error loading data',
120
+ }
121
+ : undefined,
122
+ muiTableContainerProps: {
123
+ sx: {
124
+ minHeight: '500px',
125
+ },
126
+ },
127
+ onCreatingRowCancel: () => setValidationErrors({}),
128
+ onCreatingRowSave: handleCreateUser,
129
+ onEditingRowCancel: () => setValidationErrors({}),
130
+ onEditingRowSave: handleSaveUser,
131
+ //optionally customize modal content
132
+ renderCreateRowDialogContent: ({ table, row, internalEditComponents }) => (<>
133
+ <DialogTitle variant="h3">Create New User</DialogTitle>
134
+ <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
135
+ {internalEditComponents} {/* or render custom edit components here */}
136
+ </DialogContent>
137
+ <DialogActions>
138
+ <MRT_EditActionButtons variant="text" table={table} row={row}/>
139
+ </DialogActions>
140
+ </>),
141
+ //optionally customize modal content
142
+ renderEditRowDialogContent: ({ table, row, internalEditComponents }) => (<>
143
+ <DialogTitle variant="h3">Edit User</DialogTitle>
144
+ <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
145
+ {internalEditComponents} {/* or render custom edit components here */}
146
+ </DialogContent>
147
+ <DialogActions>
148
+ <MRT_EditActionButtons variant="text" table={table} row={row}/>
149
+ </DialogActions>
150
+ </>),
151
+ renderRowActions: ({ row, table }) => (<Box sx={{ display: 'flex', gap: '1rem' }}>
152
+ <Tooltip title="Edit">
153
+ <IconButton onClick={() => table.setEditingRow(row)}>
154
+ <EditIcon />
155
+ </IconButton>
156
+ </Tooltip>
157
+ <Tooltip title="Delete">
158
+ <IconButton color="error" onClick={() => openDeleteConfirmModal(row)}>
159
+ <DeleteIcon />
160
+ </IconButton>
161
+ </Tooltip>
162
+ </Box>),
163
+ renderTopToolbarCustomActions: ({ table }) => (<Button variant="contained" onClick={() => {
164
+ table.setCreatingRow(true); //simplest way to open the create row modal with no default values
165
+ //or you can pass in a row object to set default values with the `createRow` helper function
166
+ // table.setCreatingRow(
167
+ // createRow(table, {
168
+ // //optionally pass in default values for the new row, useful for nested data or other complex scenarios
169
+ // }),
170
+ // );
171
+ }}>
172
+ Create New User
173
+ </Button>),
174
+ state: {
175
+ isLoading: isLoadingUsers,
176
+ isSaving: isCreatingUser || isUpdatingUser || isDeletingUser,
177
+ showAlertBanner: isLoadingUsersError,
178
+ showProgressBars: isFetchingUsers,
179
+ },
180
+ });
181
+ return <MaterialReactTable table={table}/>;
182
+ };
183
+ //CREATE hook (post new user to api)
184
+ function useCreateUser() {
185
+ const queryClient = useQueryClient();
186
+ return useMutation({
187
+ mutationFn: async (user) => {
188
+ //send api update request here
189
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
190
+ return Promise.resolve();
191
+ },
192
+ //client side optimistic update
193
+ onMutate: (newUserInfo) => {
194
+ queryClient.setQueryData(['users'], (prevUsers) => [
195
+ ...prevUsers,
196
+ {
197
+ ...newUserInfo,
198
+ id: (Math.random() + 1).toString(36).substring(7),
199
+ },
200
+ ]);
201
+ },
202
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
203
+ });
204
+ }
205
+ //READ hook (get users from api)
206
+ function useGetUsers() {
207
+ return useQuery({
208
+ queryKey: ['users'],
209
+ queryFn: async () => {
210
+ //send api request here
211
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
212
+ return Promise.resolve(fakeData);
213
+ },
214
+ refetchOnWindowFocus: false,
215
+ });
216
+ }
217
+ //UPDATE hook (put user in api)
218
+ function useUpdateUser() {
219
+ const queryClient = useQueryClient();
220
+ return useMutation({
221
+ mutationFn: async (user) => {
222
+ //send api update request here
223
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
224
+ return Promise.resolve();
225
+ },
226
+ //client side optimistic update
227
+ onMutate: (newUserInfo) => {
228
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.map((prevUser) => prevUser.id === newUserInfo.id ? newUserInfo : prevUser));
229
+ },
230
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
231
+ });
232
+ }
233
+ //DELETE hook (delete user in api)
234
+ function useDeleteUser() {
235
+ const queryClient = useQueryClient();
236
+ return useMutation({
237
+ mutationFn: async (userId) => {
238
+ //send api update request here
239
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
240
+ return Promise.resolve();
241
+ },
242
+ //client side optimistic update
243
+ onMutate: (userId) => {
244
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.filter((user) => user.id !== userId));
245
+ },
246
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
247
+ });
248
+ }
249
+ //react query setup in App.tsx
250
+ const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
251
+ default: d.ReactQueryDevtools,
252
+ })));
253
+ const queryClient = new QueryClient();
254
+ export default function App() {
255
+ return (<QueryClientProvider client={queryClient}>
256
+ <Example />
257
+ <Suspense fallback={null}>
258
+ <ReactQueryDevtoolsProduction />
259
+ </Suspense>
260
+ </QueryClientProvider>);
261
+ }
262
+ const validateRequired = (value) => !!value.length;
263
+ const validateEmail = (email) => !!email.length &&
264
+ email
265
+ .toLowerCase()
266
+ .match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
267
+ function validateUser(user) {
268
+ return {
269
+ firstName: !validateRequired(user.firstName)
270
+ ? 'First Name is Required'
271
+ : '',
272
+ lastName: !validateRequired(user.lastName) ? 'Last Name is Required' : '',
273
+ email: !validateEmail(user.email) ? 'Incorrect Email Format' : '',
274
+ };
275
+ }
@@ -0,0 +1,370 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import {
3
+ MRT_EditActionButtons,
4
+ MaterialReactTable,
5
+ // createRow,
6
+ type MRT_ColumnDef,
7
+ type MRT_Row,
8
+ type MRT_TableOptions,
9
+ useMaterialReactTable,
10
+ } from 'material-react-table';
11
+ import {
12
+ Box,
13
+ Button,
14
+ DialogActions,
15
+ DialogContent,
16
+ DialogTitle,
17
+ IconButton,
18
+ Tooltip,
19
+ } from '@mui/material';
20
+ import {
21
+ QueryClient,
22
+ QueryClientProvider,
23
+ useMutation,
24
+ useQuery,
25
+ useQueryClient,
26
+ } from '@tanstack/react-query';
27
+ import { type User, fakeData, usStates } from './makeData';
28
+ import EditIcon from '@mui/icons-material/Edit';
29
+ import DeleteIcon from '@mui/icons-material/Delete';
30
+
31
+ const Example = () => {
32
+ const [validationErrors, setValidationErrors] = useState<
33
+ Record<string, string | undefined>
34
+ >({});
35
+
36
+ const columns = useMemo<MRT_ColumnDef<User>[]>(
37
+ () => [
38
+ {
39
+ accessorKey: 'id',
40
+ header: 'Id',
41
+ enableEditing: false,
42
+ size: 80,
43
+ },
44
+ {
45
+ accessorKey: 'firstName',
46
+ header: 'First Name',
47
+ muiEditTextFieldProps: {
48
+ required: true,
49
+ error: !!validationErrors?.firstName,
50
+ helperText: validationErrors?.firstName,
51
+ //remove any previous validation errors when user focuses on the input
52
+ onFocus: () =>
53
+ setValidationErrors({
54
+ ...validationErrors,
55
+ firstName: undefined,
56
+ }),
57
+ //optionally add validation checking for onBlur or onChange
58
+ },
59
+ },
60
+ {
61
+ accessorKey: 'lastName',
62
+ header: 'Last Name',
63
+ muiEditTextFieldProps: {
64
+ required: true,
65
+ error: !!validationErrors?.lastName,
66
+ helperText: validationErrors?.lastName,
67
+ //remove any previous validation errors when user focuses on the input
68
+ onFocus: () =>
69
+ setValidationErrors({
70
+ ...validationErrors,
71
+ lastName: undefined,
72
+ }),
73
+ },
74
+ },
75
+ {
76
+ accessorKey: 'email',
77
+ header: 'Email',
78
+ muiEditTextFieldProps: {
79
+ type: 'email',
80
+ required: true,
81
+ error: !!validationErrors?.email,
82
+ helperText: validationErrors?.email,
83
+ //remove any previous validation errors when user focuses on the input
84
+ onFocus: () =>
85
+ setValidationErrors({
86
+ ...validationErrors,
87
+ email: undefined,
88
+ }),
89
+ },
90
+ },
91
+ {
92
+ accessorKey: 'state',
93
+ header: 'State',
94
+ editVariant: 'select',
95
+ editSelectOptions: usStates,
96
+ muiEditTextFieldProps: {
97
+ select: true,
98
+ error: !!validationErrors?.state,
99
+ helperText: validationErrors?.state,
100
+ },
101
+ },
102
+ ],
103
+ [validationErrors],
104
+ );
105
+
106
+ //call CREATE hook
107
+ const { mutateAsync: createUser, isPending: isCreatingUser } =
108
+ useCreateUser();
109
+ //call READ hook
110
+ const {
111
+ data: fetchedUsers = [],
112
+ isError: isLoadingUsersError,
113
+ isFetching: isFetchingUsers,
114
+ isLoading: isLoadingUsers,
115
+ } = useGetUsers();
116
+ //call UPDATE hook
117
+ const { mutateAsync: updateUser, isPending: isUpdatingUser } =
118
+ useUpdateUser();
119
+ //call DELETE hook
120
+ const { mutateAsync: deleteUser, isPending: isDeletingUser } =
121
+ useDeleteUser();
122
+
123
+ //CREATE action
124
+ const handleCreateUser: MRT_TableOptions<User>['onCreatingRowSave'] = async ({
125
+ values,
126
+ table,
127
+ }) => {
128
+ const newValidationErrors = validateUser(values);
129
+ if (Object.values(newValidationErrors).some((error) => error)) {
130
+ setValidationErrors(newValidationErrors);
131
+ return;
132
+ }
133
+ setValidationErrors({});
134
+ await createUser(values);
135
+ table.setCreatingRow(null); //exit creating mode
136
+ };
137
+
138
+ //UPDATE action
139
+ const handleSaveUser: MRT_TableOptions<User>['onEditingRowSave'] = async ({
140
+ values,
141
+ table,
142
+ }) => {
143
+ const newValidationErrors = validateUser(values);
144
+ if (Object.values(newValidationErrors).some((error) => error)) {
145
+ setValidationErrors(newValidationErrors);
146
+ return;
147
+ }
148
+ setValidationErrors({});
149
+ await updateUser(values);
150
+ table.setEditingRow(null); //exit editing mode
151
+ };
152
+
153
+ //DELETE action
154
+ const openDeleteConfirmModal = (row: MRT_Row<User>) => {
155
+ if (window.confirm('Are you sure you want to delete this user?')) {
156
+ deleteUser(row.original.id);
157
+ }
158
+ };
159
+
160
+ const table = useMaterialReactTable({
161
+ columns,
162
+ data: fetchedUsers,
163
+ createDisplayMode: 'modal', //default ('row', and 'custom' are also available)
164
+ editDisplayMode: 'modal', //default ('row', 'cell', 'table', and 'custom' are also available)
165
+ enableEditing: true,
166
+ getRowId: (row) => row.id,
167
+ muiToolbarAlertBannerProps: isLoadingUsersError
168
+ ? {
169
+ color: 'error',
170
+ children: 'Error loading data',
171
+ }
172
+ : undefined,
173
+ muiTableContainerProps: {
174
+ sx: {
175
+ minHeight: '500px',
176
+ },
177
+ },
178
+ onCreatingRowCancel: () => setValidationErrors({}),
179
+ onCreatingRowSave: handleCreateUser,
180
+ onEditingRowCancel: () => setValidationErrors({}),
181
+ onEditingRowSave: handleSaveUser,
182
+ //optionally customize modal content
183
+ renderCreateRowDialogContent: ({ table, row, internalEditComponents }) => (
184
+ <>
185
+ <DialogTitle variant="h3">Create New User</DialogTitle>
186
+ <DialogContent
187
+ sx={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
188
+ >
189
+ {internalEditComponents} {/* or render custom edit components here */}
190
+ </DialogContent>
191
+ <DialogActions>
192
+ <MRT_EditActionButtons variant="text" table={table} row={row} />
193
+ </DialogActions>
194
+ </>
195
+ ),
196
+ //optionally customize modal content
197
+ renderEditRowDialogContent: ({ table, row, internalEditComponents }) => (
198
+ <>
199
+ <DialogTitle variant="h3">Edit User</DialogTitle>
200
+ <DialogContent
201
+ sx={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}
202
+ >
203
+ {internalEditComponents} {/* or render custom edit components here */}
204
+ </DialogContent>
205
+ <DialogActions>
206
+ <MRT_EditActionButtons variant="text" table={table} row={row} />
207
+ </DialogActions>
208
+ </>
209
+ ),
210
+ renderRowActions: ({ row, table }) => (
211
+ <Box sx={{ display: 'flex', gap: '1rem' }}>
212
+ <Tooltip title="Edit">
213
+ <IconButton onClick={() => table.setEditingRow(row)}>
214
+ <EditIcon />
215
+ </IconButton>
216
+ </Tooltip>
217
+ <Tooltip title="Delete">
218
+ <IconButton color="error" onClick={() => openDeleteConfirmModal(row)}>
219
+ <DeleteIcon />
220
+ </IconButton>
221
+ </Tooltip>
222
+ </Box>
223
+ ),
224
+ renderTopToolbarCustomActions: ({ table }) => (
225
+ <Button
226
+ variant="contained"
227
+ onClick={() => {
228
+ table.setCreatingRow(true); //simplest way to open the create row modal with no default values
229
+ //or you can pass in a row object to set default values with the `createRow` helper function
230
+ // table.setCreatingRow(
231
+ // createRow(table, {
232
+ // //optionally pass in default values for the new row, useful for nested data or other complex scenarios
233
+ // }),
234
+ // );
235
+ }}
236
+ >
237
+ Create New User
238
+ </Button>
239
+ ),
240
+ state: {
241
+ isLoading: isLoadingUsers,
242
+ isSaving: isCreatingUser || isUpdatingUser || isDeletingUser,
243
+ showAlertBanner: isLoadingUsersError,
244
+ showProgressBars: isFetchingUsers,
245
+ },
246
+ });
247
+
248
+ return <MaterialReactTable table={table} />;
249
+ };
250
+
251
+ //CREATE hook (post new user to api)
252
+ function useCreateUser() {
253
+ const queryClient = useQueryClient();
254
+ return useMutation({
255
+ mutationFn: async (user: User) => {
256
+ //send api update request here
257
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
258
+ return Promise.resolve();
259
+ },
260
+ //client side optimistic update
261
+ onMutate: (newUserInfo: User) => {
262
+ queryClient.setQueryData(
263
+ ['users'],
264
+ (prevUsers: any) =>
265
+ [
266
+ ...prevUsers,
267
+ {
268
+ ...newUserInfo,
269
+ id: (Math.random() + 1).toString(36).substring(7),
270
+ },
271
+ ] as User[],
272
+ );
273
+ },
274
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
275
+ });
276
+ }
277
+
278
+ //READ hook (get users from api)
279
+ function useGetUsers() {
280
+ return useQuery<User[]>({
281
+ queryKey: ['users'],
282
+ queryFn: async () => {
283
+ //send api request here
284
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
285
+ return Promise.resolve(fakeData);
286
+ },
287
+ refetchOnWindowFocus: false,
288
+ });
289
+ }
290
+
291
+ //UPDATE hook (put user in api)
292
+ function useUpdateUser() {
293
+ const queryClient = useQueryClient();
294
+ return useMutation({
295
+ mutationFn: async (user: User) => {
296
+ //send api update request here
297
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
298
+ return Promise.resolve();
299
+ },
300
+ //client side optimistic update
301
+ onMutate: (newUserInfo: User) => {
302
+ queryClient.setQueryData(['users'], (prevUsers: any) =>
303
+ prevUsers?.map((prevUser: User) =>
304
+ prevUser.id === newUserInfo.id ? newUserInfo : prevUser,
305
+ ),
306
+ );
307
+ },
308
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
309
+ });
310
+ }
311
+
312
+ //DELETE hook (delete user in api)
313
+ function useDeleteUser() {
314
+ const queryClient = useQueryClient();
315
+ return useMutation({
316
+ mutationFn: async (userId: string) => {
317
+ //send api update request here
318
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
319
+ return Promise.resolve();
320
+ },
321
+ //client side optimistic update
322
+ onMutate: (userId: string) => {
323
+ queryClient.setQueryData(['users'], (prevUsers: any) =>
324
+ prevUsers?.filter((user: User) => user.id !== userId),
325
+ );
326
+ },
327
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
328
+ });
329
+ }
330
+
331
+ //react query setup in App.tsx
332
+ const ReactQueryDevtoolsProduction = lazy(() =>
333
+ import('@tanstack/react-query-devtools/build/modern/production.js').then(
334
+ (d) => ({
335
+ default: d.ReactQueryDevtools,
336
+ }),
337
+ ),
338
+ );
339
+
340
+ const queryClient = new QueryClient();
341
+
342
+ export default function App() {
343
+ return (
344
+ <QueryClientProvider client={queryClient}>
345
+ <Example />
346
+ <Suspense fallback={null}>
347
+ <ReactQueryDevtoolsProduction />
348
+ </Suspense>
349
+ </QueryClientProvider>
350
+ );
351
+ }
352
+
353
+ const validateRequired = (value: string) => !!value.length;
354
+ const validateEmail = (email: string) =>
355
+ !!email.length &&
356
+ email
357
+ .toLowerCase()
358
+ .match(
359
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
360
+ );
361
+
362
+ function validateUser(user: User) {
363
+ return {
364
+ firstName: !validateRequired(user.firstName)
365
+ ? 'First Name is Required'
366
+ : '',
367
+ lastName: !validateRequired(user.lastName) ? 'Last Name is Required' : '',
368
+ email: !validateEmail(user.email) ? 'Incorrect Email Format' : '',
369
+ };
370
+ }