@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,208 @@
1
+ import {
2
+ lazy,
3
+ Suspense,
4
+ type UIEvent,
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useRef,
9
+ useState,
10
+ } from 'react';
11
+ import {
12
+ MaterialReactTable,
13
+ useMaterialReactTable,
14
+ type MRT_ColumnDef,
15
+ type MRT_ColumnFiltersState,
16
+ type MRT_SortingState,
17
+ type MRT_RowVirtualizer,
18
+ } from 'material-react-table';
19
+ import { Typography } from '@mui/material';
20
+ import {
21
+ QueryClient,
22
+ QueryClientProvider,
23
+ useInfiniteQuery,
24
+ } from '@tanstack/react-query'; //Note: this is TanStack React Query V5
25
+
26
+ //Your API response shape will probably be different. Knowing a total row count is important though.
27
+ type UserApiResponse = {
28
+ data: Array<User>;
29
+ meta: {
30
+ totalRowCount: number;
31
+ };
32
+ };
33
+
34
+ type User = {
35
+ firstName: string;
36
+ lastName: string;
37
+ address: string;
38
+ state: string;
39
+ phoneNumber: string;
40
+ };
41
+
42
+ const columns: MRT_ColumnDef<User>[] = [
43
+ {
44
+ accessorKey: 'firstName',
45
+ header: 'First Name',
46
+ },
47
+ {
48
+ accessorKey: 'lastName',
49
+ header: 'Last Name',
50
+ },
51
+ {
52
+ accessorKey: 'address',
53
+ header: 'Address',
54
+ },
55
+ {
56
+ accessorKey: 'state',
57
+ header: 'State',
58
+ },
59
+ {
60
+ accessorKey: 'phoneNumber',
61
+ header: 'Phone Number',
62
+ },
63
+ ];
64
+
65
+ const fetchSize = 25;
66
+
67
+ const Example = () => {
68
+ const tableContainerRef = useRef<HTMLDivElement>(null); //we can get access to the underlying TableContainer element and react to its scroll events
69
+ const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null); //we can get access to the underlying Virtualizer instance and call its scrollToIndex method
70
+
71
+ const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
72
+ [],
73
+ );
74
+ const [globalFilter, setGlobalFilter] = useState<string>();
75
+ const [sorting, setSorting] = useState<MRT_SortingState>([]);
76
+
77
+ const { data, fetchNextPage, isError, isFetching, isLoading } =
78
+ useInfiniteQuery<UserApiResponse>({
79
+ queryKey: [
80
+ 'users-list',
81
+ {
82
+ columnFilters, //refetch when columnFilters changes
83
+ globalFilter, //refetch when globalFilter changes
84
+ sorting, //refetch when sorting changes
85
+ },
86
+ ],
87
+ queryFn: async ({ pageParam }) => {
88
+ const url = new URL('/api/data', location.origin); // nextjs api route
89
+ url.searchParams.set('start', `${(pageParam as number) * fetchSize}`);
90
+ url.searchParams.set('size', `${fetchSize}`);
91
+ url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
92
+ url.searchParams.set('globalFilter', globalFilter ?? '');
93
+ url.searchParams.set('sorting', JSON.stringify(sorting ?? []));
94
+
95
+ const response = await fetch(url.href);
96
+ const json = (await response.json()) as UserApiResponse;
97
+ return json;
98
+ },
99
+ initialPageParam: 0,
100
+ getNextPageParam: (_lastGroup, groups) => groups.length,
101
+ refetchOnWindowFocus: false,
102
+ });
103
+
104
+ const flatData = useMemo(
105
+ () => data?.pages.flatMap((page) => page.data) ?? [],
106
+ [data],
107
+ );
108
+
109
+ const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0;
110
+ const totalFetched = flatData.length;
111
+
112
+ //called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
113
+ const fetchMoreOnBottomReached = useCallback(
114
+ (containerRefElement?: HTMLDivElement | null) => {
115
+ if (containerRefElement) {
116
+ const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
117
+ //once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
118
+ if (
119
+ scrollHeight - scrollTop - clientHeight < 400 &&
120
+ !isFetching &&
121
+ totalFetched < totalDBRowCount
122
+ ) {
123
+ fetchNextPage();
124
+ }
125
+ }
126
+ },
127
+ [fetchNextPage, isFetching, totalFetched, totalDBRowCount],
128
+ );
129
+
130
+ //scroll to top of table when sorting or filters change
131
+ useEffect(() => {
132
+ //scroll to the top of the table when the sorting changes
133
+ try {
134
+ rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
135
+ } catch (error) {
136
+ console.error(error);
137
+ }
138
+ }, [sorting, columnFilters, globalFilter]);
139
+
140
+ //a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
141
+ useEffect(() => {
142
+ fetchMoreOnBottomReached(tableContainerRef.current);
143
+ }, [fetchMoreOnBottomReached]);
144
+
145
+ const table = useMaterialReactTable({
146
+ columns,
147
+ data: flatData,
148
+ enablePagination: false,
149
+ enableRowNumbers: true,
150
+ enableRowVirtualization: true,
151
+ manualFiltering: true,
152
+ manualSorting: true,
153
+ muiTableContainerProps: {
154
+ ref: tableContainerRef, //get access to the table container element
155
+ sx: { maxHeight: '600px' }, //give the table a max height
156
+ onScroll: (event: UIEvent<HTMLDivElement>) =>
157
+ fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
158
+ },
159
+ muiToolbarAlertBannerProps: isError
160
+ ? {
161
+ color: 'error',
162
+ children: 'Error loading data',
163
+ }
164
+ : undefined,
165
+ onColumnFiltersChange: setColumnFilters,
166
+ onGlobalFilterChange: setGlobalFilter,
167
+ onSortingChange: setSorting,
168
+ renderBottomToolbarCustomActions: () => (
169
+ <Typography>
170
+ Fetched {totalFetched} of {totalDBRowCount} total rows.
171
+ </Typography>
172
+ ),
173
+ state: {
174
+ columnFilters,
175
+ globalFilter,
176
+ isLoading,
177
+ showAlertBanner: isError,
178
+ showProgressBars: isFetching,
179
+ sorting,
180
+ },
181
+ rowVirtualizerInstanceRef, //get access to the virtualizer instance
182
+ rowVirtualizerOptions: { overscan: 4 },
183
+ });
184
+
185
+ return <MaterialReactTable table={table} />;
186
+ };
187
+
188
+ //react query setup in App.tsx
189
+ const ReactQueryDevtoolsProduction = lazy(() =>
190
+ import('@tanstack/react-query-devtools/build/modern/production.js').then(
191
+ (d) => ({
192
+ default: d.ReactQueryDevtools,
193
+ }),
194
+ ),
195
+ );
196
+
197
+ const queryClient = new QueryClient();
198
+
199
+ export default function App() {
200
+ return (
201
+ <QueryClientProvider client={queryClient}>
202
+ <Example />
203
+ <Suspense fallback={null}>
204
+ <ReactQueryDevtoolsProduction />
205
+ </Suspense>
206
+ </QueryClientProvider>
207
+ );
208
+ }
@@ -0,0 +1,164 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { Alert, CircularProgress, Stack } from '@mui/material';
4
+ import AddIcon from '@mui/icons-material/Add';
5
+ import MinusIcon from '@mui/icons-material/Remove';
6
+ import { QueryClient, QueryClientProvider, keepPreviousData, useQuery, } from '@tanstack/react-query'; //note: this is TanStack React Query V5
7
+ const DetailPanel = ({ row }) => {
8
+ const { data: userInfo, isLoading, isError, } = useFetchUserInfo({
9
+ phoneNumber: row.id, //the row id is set to the user's phone number
10
+ }, {
11
+ enabled: row.getIsExpanded(),
12
+ });
13
+ if (isLoading)
14
+ return <CircularProgress />;
15
+ if (isError)
16
+ return <Alert severity="error">Error Loading User Info</Alert>;
17
+ const { favoriteMusic, favoriteSong, quote } = userInfo ?? {};
18
+ return (<Stack sx={{
19
+ gap: '0.5rem',
20
+ minHeight: '00px',
21
+ }}>
22
+ <div>
23
+ <b>Favorite Music:</b> {favoriteMusic}
24
+ </div>
25
+ <div>
26
+ <b>Favorite Song:</b> {favoriteSong}
27
+ </div>
28
+ <div>
29
+ <b>Quote:</b> {quote}
30
+ </div>
31
+ </Stack>);
32
+ };
33
+ const Example = () => {
34
+ //manage our own state for stuff we want to pass to the API
35
+ const [columnFilters, setColumnFilters] = useState([]);
36
+ const [globalFilter, setGlobalFilter] = useState('');
37
+ const [sorting, setSorting] = useState([]);
38
+ const [pagination, setPagination] = useState({
39
+ pageIndex: 0,
40
+ pageSize: 5,
41
+ });
42
+ const { data: { data = [], meta } = {}, isError, isRefetching, isLoading, } = useFetchUsers({
43
+ columnFilters,
44
+ globalFilter,
45
+ pagination,
46
+ sorting,
47
+ });
48
+ const columns = useMemo(
49
+ //column definitions...
50
+ () => [
51
+ {
52
+ accessorKey: 'firstName',
53
+ header: 'First Name',
54
+ },
55
+ {
56
+ accessorKey: 'lastName',
57
+ header: 'Last Name',
58
+ },
59
+ {
60
+ accessorKey: 'address',
61
+ header: 'Address',
62
+ },
63
+ {
64
+ accessorKey: 'state',
65
+ header: 'State',
66
+ },
67
+ {
68
+ accessorKey: 'phoneNumber',
69
+ header: 'Phone Number',
70
+ },
71
+ ], []);
72
+ const table = useMaterialReactTable({
73
+ columns,
74
+ data,
75
+ getRowId: (row) => row.phoneNumber,
76
+ manualFiltering: true, //turn off built-in client-side filtering
77
+ manualPagination: true, //turn off built-in client-side pagination
78
+ manualSorting: true, //turn off built-in client-side sorting
79
+ muiExpandButtonProps: ({ row }) => ({
80
+ children: row.getIsExpanded() ? <MinusIcon /> : <AddIcon />,
81
+ }),
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
+ renderDetailPanel: ({ row }) => <DetailPanel row={row}/>,
93
+ rowCount: meta?.totalRowCount ?? 0,
94
+ state: {
95
+ columnFilters,
96
+ globalFilter,
97
+ isLoading,
98
+ pagination,
99
+ showAlertBanner: isError,
100
+ showProgressBars: isRefetching,
101
+ sorting,
102
+ },
103
+ });
104
+ return <MaterialReactTable table={table}/>;
105
+ };
106
+ //react query setup in App.tsx
107
+ const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
108
+ default: d.ReactQueryDevtools,
109
+ })));
110
+ const queryClient = new QueryClient();
111
+ export default function App() {
112
+ return (<QueryClientProvider client={queryClient}>
113
+ <Example />
114
+ <Suspense fallback={null}>
115
+ <ReactQueryDevtoolsProduction />
116
+ </Suspense>
117
+ </QueryClientProvider>);
118
+ }
119
+ //fetch user hook
120
+ const useFetchUsers = ({ columnFilters, globalFilter, pagination, sorting, }) => {
121
+ return useQuery({
122
+ queryKey: [
123
+ 'users', //give a unique key for this query
124
+ columnFilters, //refetch when columnFilters changes
125
+ globalFilter, //refetch when globalFilter changes
126
+ pagination.pageIndex, //refetch when pagination.pageIndex changes
127
+ pagination.pageSize, //refetch when pagination.pageSize changes
128
+ sorting, //refetch when sorting changes
129
+ ],
130
+ queryFn: async () => {
131
+ const fetchURL = new URL('/api/data', location.origin);
132
+ //read our state and pass it to the API as query params
133
+ fetchURL.searchParams.set('start', `${pagination.pageIndex * pagination.pageSize}`);
134
+ fetchURL.searchParams.set('size', `${pagination.pageSize}`);
135
+ fetchURL.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
136
+ fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
137
+ fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
138
+ //use whatever fetch library you want, fetch, axios, etc
139
+ const response = await fetch(fetchURL.href);
140
+ const json = (await response.json());
141
+ return json;
142
+ },
143
+ placeholderData: keepPreviousData, //don't go to 0 rows when refetching or paginating to next page
144
+ });
145
+ };
146
+ //fetch more user info hook
147
+ const useFetchUserInfo = (params, options) => {
148
+ return useQuery({
149
+ enabled: options.enabled, //only fetch when the detail panel is opened
150
+ staleTime: 60 * 1000, //don't refetch for 60 seconds
151
+ queryKey: ['user', params.phoneNumber], //give a unique key for this query for each user fetch
152
+ queryFn: async () => {
153
+ const fetchURL = new URL(`/api/moredata/${params.phoneNumber
154
+ .replaceAll('-', '')
155
+ .replaceAll('.', '')
156
+ .replaceAll('(', '')
157
+ .replaceAll(')', '')}`, location.origin);
158
+ //use whatever fetch library you want, fetch, axios, etc
159
+ const response = await fetch(fetchURL.href);
160
+ const json = (await response.json());
161
+ return json;
162
+ },
163
+ });
164
+ };
@@ -0,0 +1,262 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import {
3
+ MaterialReactTable,
4
+ useMaterialReactTable,
5
+ type MRT_ColumnDef,
6
+ type MRT_ColumnFiltersState,
7
+ type MRT_PaginationState,
8
+ type MRT_SortingState,
9
+ type MRT_Row,
10
+ } from 'material-react-table';
11
+ import { Alert, CircularProgress, Stack } from '@mui/material';
12
+ import AddIcon from '@mui/icons-material/Add';
13
+ import MinusIcon from '@mui/icons-material/Remove';
14
+ import {
15
+ QueryClient,
16
+ QueryClientProvider,
17
+ keepPreviousData,
18
+ useQuery,
19
+ } from '@tanstack/react-query'; //note: this is TanStack React Query V5
20
+
21
+ //Your API response shape will probably be different. Knowing a total row count is important though.
22
+ type UserApiResponse = {
23
+ data: Array<User>;
24
+ meta: {
25
+ totalRowCount: number;
26
+ };
27
+ };
28
+
29
+ type User = {
30
+ firstName: string;
31
+ lastName: string;
32
+ address: string;
33
+ state: string;
34
+ phoneNumber: string;
35
+ lastLogin: Date;
36
+ };
37
+
38
+ type FullUserInfoApiResponse = FullUserInfo;
39
+
40
+ type FullUserInfo = User & {
41
+ favoriteMusic: string;
42
+ favoriteSong: string;
43
+ quote: string;
44
+ };
45
+
46
+ const DetailPanel = ({ row }: { row: MRT_Row<User> }) => {
47
+ const {
48
+ data: userInfo,
49
+ isLoading,
50
+ isError,
51
+ } = useFetchUserInfo(
52
+ {
53
+ phoneNumber: row.id, //the row id is set to the user's phone number
54
+ },
55
+ {
56
+ enabled: row.getIsExpanded(),
57
+ },
58
+ );
59
+ if (isLoading) return <CircularProgress />;
60
+ if (isError) return <Alert severity="error">Error Loading User Info</Alert>;
61
+
62
+ const { favoriteMusic, favoriteSong, quote } = userInfo ?? {};
63
+
64
+ return (
65
+ <Stack
66
+ sx={{
67
+ gap: '0.5rem',
68
+ minHeight: '00px',
69
+ }}
70
+ >
71
+ <div>
72
+ <b>Favorite Music:</b> {favoriteMusic}
73
+ </div>
74
+ <div>
75
+ <b>Favorite Song:</b> {favoriteSong}
76
+ </div>
77
+ <div>
78
+ <b>Quote:</b> {quote}
79
+ </div>
80
+ </Stack>
81
+ );
82
+ };
83
+
84
+ const Example = () => {
85
+ //manage our own state for stuff we want to pass to the API
86
+ const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
87
+ [],
88
+ );
89
+ const [globalFilter, setGlobalFilter] = useState('');
90
+ const [sorting, setSorting] = useState<MRT_SortingState>([]);
91
+ const [pagination, setPagination] = useState<MRT_PaginationState>({
92
+ pageIndex: 0,
93
+ pageSize: 5,
94
+ });
95
+
96
+ const {
97
+ data: { data = [], meta } = {},
98
+ isError,
99
+ isRefetching,
100
+ isLoading,
101
+ } = useFetchUsers({
102
+ columnFilters,
103
+ globalFilter,
104
+ pagination,
105
+ sorting,
106
+ });
107
+
108
+ const columns = useMemo<MRT_ColumnDef<User>[]>(
109
+ //column definitions...
110
+ () => [
111
+ {
112
+ accessorKey: 'firstName',
113
+ header: 'First Name',
114
+ },
115
+ {
116
+ accessorKey: 'lastName',
117
+ header: 'Last Name',
118
+ },
119
+ {
120
+ accessorKey: 'address',
121
+ header: 'Address',
122
+ },
123
+ {
124
+ accessorKey: 'state',
125
+ header: 'State',
126
+ },
127
+ {
128
+ accessorKey: 'phoneNumber',
129
+ header: 'Phone Number',
130
+ },
131
+ ],
132
+ [],
133
+ //end
134
+ );
135
+
136
+ const table = useMaterialReactTable({
137
+ columns,
138
+ data,
139
+ getRowId: (row) => row.phoneNumber,
140
+ manualFiltering: true, //turn off built-in client-side filtering
141
+ manualPagination: true, //turn off built-in client-side pagination
142
+ manualSorting: true, //turn off built-in client-side sorting
143
+ muiExpandButtonProps: ({ row }) => ({
144
+ children: row.getIsExpanded() ? <MinusIcon /> : <AddIcon />,
145
+ }),
146
+ muiToolbarAlertBannerProps: isError
147
+ ? {
148
+ color: 'error',
149
+ children: 'Error loading data',
150
+ }
151
+ : undefined,
152
+ onColumnFiltersChange: setColumnFilters,
153
+ onGlobalFilterChange: setGlobalFilter,
154
+ onPaginationChange: setPagination,
155
+ onSortingChange: setSorting,
156
+ renderDetailPanel: ({ row }) => <DetailPanel row={row} />,
157
+ rowCount: meta?.totalRowCount ?? 0,
158
+ state: {
159
+ columnFilters,
160
+ globalFilter,
161
+ isLoading,
162
+ pagination,
163
+ showAlertBanner: isError,
164
+ showProgressBars: isRefetching,
165
+ sorting,
166
+ },
167
+ });
168
+
169
+ return <MaterialReactTable table={table} />;
170
+ };
171
+
172
+ //react query setup in App.tsx
173
+ const ReactQueryDevtoolsProduction = lazy(() =>
174
+ import('@tanstack/react-query-devtools/build/modern/production.js').then(
175
+ (d) => ({
176
+ default: d.ReactQueryDevtools,
177
+ }),
178
+ ),
179
+ );
180
+
181
+ const queryClient = new QueryClient();
182
+
183
+ export default function App() {
184
+ return (
185
+ <QueryClientProvider client={queryClient}>
186
+ <Example />
187
+ <Suspense fallback={null}>
188
+ <ReactQueryDevtoolsProduction />
189
+ </Suspense>
190
+ </QueryClientProvider>
191
+ );
192
+ }
193
+
194
+ //fetch user hook
195
+ const useFetchUsers = ({
196
+ columnFilters,
197
+ globalFilter,
198
+ pagination,
199
+ sorting,
200
+ }: {
201
+ columnFilters: MRT_ColumnFiltersState;
202
+ globalFilter: string;
203
+ pagination: MRT_PaginationState;
204
+ sorting: MRT_SortingState;
205
+ }) => {
206
+ return useQuery<UserApiResponse>({
207
+ queryKey: [
208
+ 'users', //give a unique key for this query
209
+ columnFilters, //refetch when columnFilters changes
210
+ globalFilter, //refetch when globalFilter changes
211
+ pagination.pageIndex, //refetch when pagination.pageIndex changes
212
+ pagination.pageSize, //refetch when pagination.pageSize changes
213
+ sorting, //refetch when sorting changes
214
+ ],
215
+ queryFn: async () => {
216
+ const fetchURL = new URL('/api/data', location.origin);
217
+
218
+ //read our state and pass it to the API as query params
219
+ fetchURL.searchParams.set(
220
+ 'start',
221
+ `${pagination.pageIndex * pagination.pageSize}`,
222
+ );
223
+ fetchURL.searchParams.set('size', `${pagination.pageSize}`);
224
+ fetchURL.searchParams.set('filters', JSON.stringify(columnFilters ?? []));
225
+ fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
226
+ fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
227
+
228
+ //use whatever fetch library you want, fetch, axios, etc
229
+ const response = await fetch(fetchURL.href);
230
+ const json = (await response.json()) as UserApiResponse;
231
+ return json;
232
+ },
233
+ placeholderData: keepPreviousData, //don't go to 0 rows when refetching or paginating to next page
234
+ });
235
+ };
236
+
237
+ //fetch more user info hook
238
+ const useFetchUserInfo = (
239
+ params: { phoneNumber: string },
240
+ options: { enabled: boolean },
241
+ ) => {
242
+ return useQuery<FullUserInfoApiResponse>({
243
+ enabled: options.enabled, //only fetch when the detail panel is opened
244
+ staleTime: 60 * 1000, //don't refetch for 60 seconds
245
+ queryKey: ['user', params.phoneNumber], //give a unique key for this query for each user fetch
246
+ queryFn: async () => {
247
+ const fetchURL = new URL(
248
+ `/api/moredata/${params.phoneNumber
249
+ .replaceAll('-', '')
250
+ .replaceAll('.', '')
251
+ .replaceAll('(', '')
252
+ .replaceAll(')', '')}`,
253
+ location.origin,
254
+ );
255
+
256
+ //use whatever fetch library you want, fetch, axios, etc
257
+ const response = await fetch(fetchURL.href);
258
+ const json = (await response.json()) as FullUserInfoApiResponse;
259
+ return json;
260
+ },
261
+ });
262
+ };