@mini_7/material-react-table-mcp 4.0.0
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.
- package/README.md +68 -0
- package/data/MIGRATION.md +136 -0
- package/data/api.json +5379 -0
- package/data/examples/advanced.tsx +309 -0
- package/data/examples/aggregation-and-grouping.tsx +153 -0
- package/data/examples/aggregation-multi.tsx +105 -0
- package/data/examples/alternate-column-filtering.tsx +49 -0
- package/data/examples/alternate-detail-panel.tsx +81 -0
- package/data/examples/alternate-pagination.tsx +23 -0
- package/data/examples/basic.tsx +109 -0
- package/data/examples/chart-detail-panel.tsx +89 -0
- package/data/examples/column-actions-space.tsx +44 -0
- package/data/examples/column-alignment.tsx +70 -0
- package/data/examples/custom-column-actions.tsx +109 -0
- package/data/examples/custom-column-filtering-ui.tsx +89 -0
- package/data/examples/custom-headless.tsx +125 -0
- package/data/examples/custom-top-toolbar.tsx +88 -0
- package/data/examples/customize-display-columns.tsx +85 -0
- package/data/examples/customize-filter-components.tsx +101 -0
- package/data/examples/customize-filter-modes.tsx +73 -0
- package/data/examples/customize-filter-variants.tsx +116 -0
- package/data/examples/customize-global-filter-component.tsx +52 -0
- package/data/examples/customize-remove-column-grouping.tsx +94 -0
- package/data/examples/customize-row-selection.tsx +54 -0
- package/data/examples/customize-table-styles.tsx +100 -0
- package/data/examples/disable-column-actions.tsx +37 -0
- package/data/examples/disable-column-hiding.tsx +90 -0
- package/data/examples/disable-density-toggle.tsx +49 -0
- package/data/examples/dynamic-columns.tsx +189 -0
- package/data/examples/editing-crud-cell.tsx +377 -0
- package/data/examples/editing-crud-modal.tsx +370 -0
- package/data/examples/editing-crud-row.tsx +333 -0
- package/data/examples/editing-crud-table.tsx +369 -0
- package/data/examples/editing-crud-tree.tsx +416 -0
- package/data/examples/enable-cell-actions.tsx +76 -0
- package/data/examples/enable-click-to-copy.tsx +39 -0
- package/data/examples/enable-column-grouping.tsx +120 -0
- package/data/examples/enable-column-ordering.tsx +48 -0
- package/data/examples/enable-column-pinning.tsx +69 -0
- package/data/examples/enable-column-resizing.tsx +56 -0
- package/data/examples/enable-column-virtualization.tsx +27 -0
- package/data/examples/enable-detail-panel-conditionally.tsx +78 -0
- package/data/examples/enable-detail-panel-virtualized.tsx +80 -0
- package/data/examples/enable-expanding-tree.tsx +110 -0
- package/data/examples/enable-filter-facet-values.tsx +65 -0
- package/data/examples/enable-row-dragging.tsx +137 -0
- package/data/examples/enable-row-numbers-original.tsx +48 -0
- package/data/examples/enable-row-numbers-static.tsx +48 -0
- package/data/examples/enable-row-ordering.tsx +57 -0
- package/data/examples/enable-row-pinning-select.tsx +76 -0
- package/data/examples/enable-row-pinning-static.tsx +50 -0
- package/data/examples/enable-row-pinning-sticky.tsx +69 -0
- package/data/examples/enable-row-selection.tsx +96 -0
- package/data/examples/enable-row-virtualization.tsx +101 -0
- package/data/examples/enable-sticky-header.tsx +59 -0
- package/data/examples/expanding-tree-expanded.tsx +121 -0
- package/data/examples/expanding-tree-flat-parse.tsx +125 -0
- package/data/examples/expanding-tree-root-expanded.tsx +151 -0
- package/data/examples/export-to-csv.tsx +114 -0
- package/data/examples/export-to-pdf.tsx +104 -0
- package/data/examples/external-toolbar.tsx +108 -0
- package/data/examples/font-awesome-icons.tsx +106 -0
- package/data/examples/infinite-scrolling.tsx +208 -0
- package/data/examples/lazy-detail-panel.tsx +262 -0
- package/data/examples/lazy-sub-rows.tsx +190 -0
- package/data/examples/linear-progress.tsx +66 -0
- package/data/examples/loading.tsx +50 -0
- package/data/examples/localization-i18n-ar.tsx +65 -0
- package/data/examples/localization-i18n-az.tsx +60 -0
- package/data/examples/localization-i18n-bg.tsx +60 -0
- package/data/examples/localization-i18n-cs.tsx +60 -0
- package/data/examples/localization-i18n-da.tsx +60 -0
- package/data/examples/localization-i18n-de.tsx +60 -0
- package/data/examples/localization-i18n-el.tsx +60 -0
- package/data/examples/localization-i18n-en.tsx +60 -0
- package/data/examples/localization-i18n-es.tsx +60 -0
- package/data/examples/localization-i18n-et.tsx +60 -0
- package/data/examples/localization-i18n-fa.tsx +66 -0
- package/data/examples/localization-i18n-fi.tsx +60 -0
- package/data/examples/localization-i18n-fr.tsx +60 -0
- package/data/examples/localization-i18n-he.tsx +66 -0
- package/data/examples/localization-i18n-hr.tsx +60 -0
- package/data/examples/localization-i18n-hu.tsx +60 -0
- package/data/examples/localization-i18n-hy.tsx +65 -0
- package/data/examples/localization-i18n-id.tsx +60 -0
- package/data/examples/localization-i18n-it.tsx +60 -0
- package/data/examples/localization-i18n-ja.tsx +60 -0
- package/data/examples/localization-i18n-ko.tsx +60 -0
- package/data/examples/localization-i18n-nl.tsx +60 -0
- package/data/examples/localization-i18n-no.tsx +60 -0
- package/data/examples/localization-i18n-np.tsx +60 -0
- package/data/examples/localization-i18n-pl.tsx +60 -0
- package/data/examples/localization-i18n-pt-BR.tsx +60 -0
- package/data/examples/localization-i18n-pt.tsx +60 -0
- package/data/examples/localization-i18n-ro.tsx +60 -0
- package/data/examples/localization-i18n-ru.tsx +60 -0
- package/data/examples/localization-i18n-sk.tsx +60 -0
- package/data/examples/localization-i18n-sr-Cyrl-RS.tsx +60 -0
- package/data/examples/localization-i18n-sr-Latn-RS.tsx +60 -0
- package/data/examples/localization-i18n-sv.tsx +60 -0
- package/data/examples/localization-i18n-tr.tsx +60 -0
- package/data/examples/localization-i18n-uk.tsx +60 -0
- package/data/examples/localization-i18n-vi.tsx +60 -0
- package/data/examples/localization-i18n-zh-hans.tsx +60 -0
- package/data/examples/localization-i18n-zh-hant.tsx +60 -0
- package/data/examples/manual-selection.tsx +105 -0
- package/data/examples/minimal.tsx +78 -0
- package/data/examples/mui-theme.tsx +136 -0
- package/data/examples/multi-sorting.tsx +58 -0
- package/data/examples/persistent-state.tsx +184 -0
- package/data/examples/react-query.tsx +182 -0
- package/data/examples/remote.tsx +150 -0
- package/data/examples/row-actions-buttons.tsx +89 -0
- package/data/examples/row-actions-menu-items.tsx +64 -0
- package/data/examples/single-row-selection.tsx +98 -0
- package/data/examples/virtualized.tsx +138 -0
- package/data/guides/accessibility.md +119 -0
- package/data/guides/aggregation.md +210 -0
- package/data/guides/async-loading.md +132 -0
- package/data/guides/best-practices.md +363 -0
- package/data/guides/cell-actions.md +139 -0
- package/data/guides/click-to-copy.md +67 -0
- package/data/guides/column-actions.md +69 -0
- package/data/guides/column-filtering.md +366 -0
- package/data/guides/column-grouping.md +192 -0
- package/data/guides/column-hiding.md +133 -0
- package/data/guides/column-ordering-dnd.md +99 -0
- package/data/guides/column-pinning.md +72 -0
- package/data/guides/column-resizing.md +157 -0
- package/data/guides/column-size.md +162 -0
- package/data/guides/customize-components.md +395 -0
- package/data/guides/customize-icons.md +35 -0
- package/data/guides/data-columns.md +292 -0
- package/data/guides/density-toggle.md +46 -0
- package/data/guides/detail-panel.md +195 -0
- package/data/guides/display-columns.md +135 -0
- package/data/guides/editing.md +367 -0
- package/data/guides/expanding-sub-rows.md +194 -0
- package/data/guides/full-screen-toggle.md +63 -0
- package/data/guides/global-filtering.md +237 -0
- package/data/guides/localization.md +57 -0
- package/data/guides/memoization.md +157 -0
- package/data/guides/pagination.md +171 -0
- package/data/guides/row-actions.md +116 -0
- package/data/guides/row-numbers.md +24 -0
- package/data/guides/row-ordering-dnd.md +51 -0
- package/data/guides/row-pinning.md +96 -0
- package/data/guides/row-selection.md +237 -0
- package/data/guides/sorting.md +190 -0
- package/data/guides/state-management.md +165 -0
- package/data/guides/sticky-header.md +41 -0
- package/data/guides/table-event-listeners.md +103 -0
- package/data/guides/toolbar-customization.md +251 -0
- package/data/guides/virtualization.md +183 -0
- package/data/index.json +1930 -0
- package/data/reference/mrt-components.md +183 -0
- package/data/reference/mrt-hooks.md +125 -0
- package/data/skills/composable-components/SKILL.md +189 -0
- package/data/skills/customization/SKILL.md +190 -0
- package/data/skills/drag-and-drop-ordering/SKILL.md +227 -0
- package/data/skills/editing/SKILL.md +243 -0
- package/data/skills/filtering/SKILL.md +213 -0
- package/data/skills/getting-started/SKILL.md +213 -0
- package/data/skills/localization/SKILL.md +182 -0
- package/data/skills/migrate-v3-to-v4/SKILL.md +161 -0
- package/data/skills/state-and-server-data/SKILL.md +200 -0
- package/data/skills/virtualization/SKILL.md +203 -0
- package/dist/data.js +137 -0
- package/dist/index.js +232 -0
- package/package.json +50 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: state-and-server-data
|
|
3
|
+
description: >
|
|
4
|
+
Control Material React Table V4 state with initialState, the state option plus on*Change callbacks, MRT_Updater handlers, table.getState and table.state, and move filtering, sorting, pagination, grouping, or expanding to the server with manual* options and rowCount. Load for controlled tables, persisted state, side effects on state change, TanStack Query integration, or infinite scrolling.
|
|
5
|
+
metadata:
|
|
6
|
+
type: framework
|
|
7
|
+
library: '@mini_7/material-react-table'
|
|
8
|
+
library_version: '4.0.0'
|
|
9
|
+
framework: react
|
|
10
|
+
requires:
|
|
11
|
+
- getting-started
|
|
12
|
+
- '@tanstack/table-core#client-vs-server'
|
|
13
|
+
sources:
|
|
14
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/pages/docs/guides/state-management.mdx'
|
|
15
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/pages/docs/guides/pagination.mdx'
|
|
16
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/pages/docs/guides/async-loading.mdx'
|
|
17
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/examples/react-query/sandbox/src/TS.tsx'
|
|
18
|
+
- 'gyminii/material-react-table:packages/material-react-table/src/types.ts'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
This skill builds on `getting-started` and `@tanstack/table-core#client-vs-server`. MRT manages every state slice internally by default. Take over only the slices you need to read or persist, and only through the `state` option paired with the matching `on*Change` callback.
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const [pagination, setPagination] = useState<MRT_PaginationState>({ pageIndex: 0, pageSize: 25 })
|
|
27
|
+
const [sorting, setSorting] = useState<MRT_SortingState>([])
|
|
28
|
+
|
|
29
|
+
const table = useMaterialReactTable({
|
|
30
|
+
columns,
|
|
31
|
+
data,
|
|
32
|
+
initialState: { density: 'compact', showColumnFilters: true },
|
|
33
|
+
state: { pagination, sorting },
|
|
34
|
+
onPaginationChange: setPagination,
|
|
35
|
+
onSortingChange: setSorting,
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`initialState` seeds slices MRT keeps managing. `state` hands a slice over to you; from then on MRT calls `on<Slice>Change` and reads only what you pass back. Do not put the same slice in both.
|
|
40
|
+
|
|
41
|
+
## Core Patterns
|
|
42
|
+
|
|
43
|
+
### Handle updaters like React setState
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { type MRT_Updater, type MRT_RowSelectionState } from '@mini_7/material-react-table'
|
|
47
|
+
|
|
48
|
+
const handleRowSelectionChange = (updater: MRT_Updater<MRT_RowSelectionState>) => {
|
|
49
|
+
setRowSelection((previous) => {
|
|
50
|
+
const next = updater instanceof Function ? updater(previous) : updater
|
|
51
|
+
onSelectionChanged(Object.keys(next)) // side effect runs once, even in Strict Mode
|
|
52
|
+
return next
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Callbacks receive either a value or an updater function, exactly like `useState`. Use `getRowId` so selection keys are stable ids instead of row indexes.
|
|
58
|
+
|
|
59
|
+
### Server-side data with manual* options
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const { data: page, isLoading, isError, isRefetching } = useQuery({
|
|
63
|
+
queryKey: ['people', pagination, sorting, columnFilters, globalFilter],
|
|
64
|
+
queryFn: () => fetchPeople({ pagination, sorting, columnFilters, globalFilter }),
|
|
65
|
+
placeholderData: keepPreviousData,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const table = useMaterialReactTable({
|
|
69
|
+
columns,
|
|
70
|
+
data: page?.rows ?? EMPTY,
|
|
71
|
+
rowCount: page?.total ?? 0,
|
|
72
|
+
manualFiltering: true,
|
|
73
|
+
manualPagination: true,
|
|
74
|
+
manualSorting: true,
|
|
75
|
+
state: { pagination, sorting, columnFilters, globalFilter, isLoading, showAlertBanner: isError, showProgressBars: isRefetching },
|
|
76
|
+
onPaginationChange: setPagination,
|
|
77
|
+
onSortingChange: setSorting,
|
|
78
|
+
onColumnFiltersChange: setColumnFilters,
|
|
79
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
80
|
+
muiToolbarAlertBannerProps: isError ? { color: 'error', children: 'Error loading data' } : undefined,
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Each `manual*` option tells MRT that stage already happened on the server, so the client row model passes rows through. `rowCount` (or `pageCount`) drives the pagination controls. `isLoading`, `showProgressBars`, `showSkeletons`, and `showLoadingOverlay` are MRT state slices meant to be set from fetch status.
|
|
85
|
+
|
|
86
|
+
### Read state without controlling it
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
const table = useMaterialReactTable({ columns, data })
|
|
90
|
+
|
|
91
|
+
<Button onClick={() => save(table.getState().columnVisibility)}>Save layout</Button>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`table.getState()` returns the complete state, including MRT-only slices such as `density`, `isFullScreen`, `showColumnFilters`, `editingRow`, and `creatingRow`. In render code `table.state` is reactive. Setters such as `table.setPagination`, `table.resetSorting`, `table.setShowColumnFilters`, and `table.setDensity` exist for every slice.
|
|
95
|
+
|
|
96
|
+
### Persist state
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
const [columnVisibility, setColumnVisibility] = useState<MRT_VisibilityState>(
|
|
100
|
+
() => JSON.parse(localStorage.getItem('people-columns') ?? '{}'),
|
|
101
|
+
)
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
localStorage.setItem('people-columns', JSON.stringify(columnVisibility))
|
|
104
|
+
}, [columnVisibility])
|
|
105
|
+
|
|
106
|
+
const table = useMaterialReactTable({
|
|
107
|
+
columns,
|
|
108
|
+
data,
|
|
109
|
+
state: { columnVisibility },
|
|
110
|
+
onColumnVisibilityChange: setColumnVisibility,
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Common Mistakes
|
|
115
|
+
|
|
116
|
+
### HIGH Passing on*Change without state
|
|
117
|
+
|
|
118
|
+
Wrong:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
const table = useMaterialReactTable({ columns, data, onSortingChange: setSorting })
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Correct:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
const table = useMaterialReactTable({
|
|
128
|
+
columns,
|
|
129
|
+
data,
|
|
130
|
+
state: { sorting },
|
|
131
|
+
onSortingChange: setSorting,
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Once a callback is supplied, MRT stops updating that slice itself. Without `state`, the table never sorts.
|
|
136
|
+
|
|
137
|
+
Source: `docs/guides/state-management.mdx`
|
|
138
|
+
|
|
139
|
+
### HIGH Toggling row selection with false
|
|
140
|
+
|
|
141
|
+
Wrong:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
setRowSelection((old) => ({ ...old, [id]: false }))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Correct:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
setRowSelection(({ [id]: _removed, ...rest }) => rest)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
TanStack Table V9 types `rowSelection` as `Record<string, true>`, so a `false` entry is a type error; absence of the key is the only deselected state.
|
|
154
|
+
|
|
155
|
+
Source: `MIGRATION.md`
|
|
156
|
+
|
|
157
|
+
### MEDIUM Using onStateChange or table.setState
|
|
158
|
+
|
|
159
|
+
Wrong:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
onStateChange: (updater) => setTableState(updater)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Correct:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
state: { pagination, sorting },
|
|
169
|
+
onPaginationChange: setPagination,
|
|
170
|
+
onSortingChange: setSorting,
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`onStateChange` and `table.setState` were removed with TanStack Table V9. Control slices individually.
|
|
174
|
+
|
|
175
|
+
Source: `MIGRATION.md`
|
|
176
|
+
|
|
177
|
+
### MEDIUM Forgetting rowCount with manualPagination
|
|
178
|
+
|
|
179
|
+
Wrong:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
manualPagination: true,
|
|
183
|
+
data: page.rows,
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Correct:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
manualPagination: true,
|
|
190
|
+
data: page.rows,
|
|
191
|
+
rowCount: page.total,
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Without a total, MRT assumes the current page is all the data and disables the next page button.
|
|
195
|
+
|
|
196
|
+
Source: `docs/guides/pagination.mdx`
|
|
197
|
+
|
|
198
|
+
## API Discovery
|
|
199
|
+
|
|
200
|
+
`MRT_TableState` in `node_modules/@mini_7/material-react-table/dist/index.d.ts` lists every slice; each has an `on<Slice>Change` option and `table.set<Slice>` method. The State Options table at `/docs/api/state-options` shows defaults, and the `manual*` and `rowCount` options are in `/docs/api/table-options`.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: virtualization
|
|
3
|
+
description: >
|
|
4
|
+
Render thousands of rows or hundreds of columns in Material React Table V4 with enableRowVirtualization and enableColumnVirtualization, tune rowVirtualizerOptions and columnVirtualizerOptions (overscan, estimateSize), reach the TanStack Virtual instance through rowVirtualizerInstanceRef for scrollToIndex, and understand the automatic layoutMode grid and sticky header. Load for large unpaginated tables, infinite scroll, scroll-to-row, jumpy scrolling, or headless virtualization with useMRT_RowVirtualizer.
|
|
5
|
+
metadata:
|
|
6
|
+
type: framework
|
|
7
|
+
library: '@mini_7/material-react-table'
|
|
8
|
+
library_version: '4.0.0'
|
|
9
|
+
framework: react
|
|
10
|
+
requires:
|
|
11
|
+
- getting-started
|
|
12
|
+
sources:
|
|
13
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/pages/docs/guides/virtualization.mdx'
|
|
14
|
+
- 'gyminii/material-react-table:apps/material-react-table-docs/examples/enable-row-virtualization/sandbox/src/TS.tsx'
|
|
15
|
+
- 'gyminii/material-react-table:packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts'
|
|
16
|
+
- 'gyminii/material-react-table:packages/material-react-table/src/hooks/useMRT_TableOptions.ts'
|
|
17
|
+
- 'gyminii/material-react-table:packages/material-react-table/src/types.ts'
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
This skill builds on `getting-started`. MRT wraps `@tanstack/react-virtual` so only the rows and columns inside the scroll container render. Enable it when a table shows more than about 50 rows without pagination or more than about a dozen columns; below that it adds overhead without benefit.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
const table = useMaterialReactTable({
|
|
26
|
+
columns,
|
|
27
|
+
data, // 10,000 rows
|
|
28
|
+
enablePagination: false,
|
|
29
|
+
enableRowVirtualization: true,
|
|
30
|
+
enableColumnVirtualization: true, // only with many columns
|
|
31
|
+
muiTableContainerProps: { sx: { maxHeight: '600px' } },
|
|
32
|
+
rowVirtualizerOptions: { overscan: 5 },
|
|
33
|
+
columnVirtualizerOptions: { overscan: 2 },
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Row virtualization sets `enableStickyHeader` to true and, unless `layoutMode` was given, switches `layoutMode` to `'grid'`, so the table renders with CSS grid and flexbox instead of semantic table layout. The container needs a bounded height for scrolling; the sticky header default caps it at 100vh, and `muiTableContainerProps.sx.maxHeight` sets a tighter one.
|
|
38
|
+
|
|
39
|
+
## Core Patterns
|
|
40
|
+
|
|
41
|
+
### Tune the virtualizer
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
rowVirtualizerOptions: ({ table }) => ({
|
|
45
|
+
overscan: 10,
|
|
46
|
+
estimateSize: () => (table.getState().density === 'compact' ? 37 : 73),
|
|
47
|
+
}),
|
|
48
|
+
columnVirtualizerOptions: { overscan: 3, estimateSize: () => 200 },
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Both options accept an object or a callback receiving `table`, and both are `Partial<VirtualizerOptions>` from TanStack Virtual. MRT already estimates row height from the density (37, 58, or 73 px) and measures rendered rows, sets `overscan: 4`, and keeps a dragging row mounted; anything you pass merges over those defaults. Raise `overscan` if fast scrolling shows blank rows, and set `estimateSize` when rows or columns are far from the defaults so the scrollbar length is accurate.
|
|
52
|
+
|
|
53
|
+
### Reach the virtualizer instance
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null)
|
|
57
|
+
const [sorting, setSorting] = useState<MRT_SortingState>([])
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0) // back to top when sorting changes
|
|
61
|
+
}, [sorting])
|
|
62
|
+
|
|
63
|
+
const table = useMaterialReactTable({
|
|
64
|
+
columns,
|
|
65
|
+
data,
|
|
66
|
+
enablePagination: false,
|
|
67
|
+
enableRowVirtualization: true,
|
|
68
|
+
rowVirtualizerInstanceRef,
|
|
69
|
+
state: { sorting },
|
|
70
|
+
onSortingChange: setSorting,
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`MRT_RowVirtualizer` and `MRT_ColumnVirtualizer` are the TanStack `Virtualizer` plus `virtualRows` or `virtualColumns`, so `scrollToIndex`, `scrollToOffset`, `measure`, and `getVirtualItems` are available. `columnVirtualizerInstanceRef` is the column counterpart.
|
|
75
|
+
|
|
76
|
+
### Infinite scroll on the container
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const tableContainerRef = useRef<HTMLDivElement>(null)
|
|
80
|
+
|
|
81
|
+
const table = useMaterialReactTable({
|
|
82
|
+
columns,
|
|
83
|
+
data: flatRows,
|
|
84
|
+
enablePagination: false,
|
|
85
|
+
enableRowVirtualization: true,
|
|
86
|
+
manualFiltering: true,
|
|
87
|
+
manualSorting: true,
|
|
88
|
+
muiTableContainerProps: {
|
|
89
|
+
ref: tableContainerRef,
|
|
90
|
+
sx: { maxHeight: '600px' },
|
|
91
|
+
onScroll: (event) => {
|
|
92
|
+
const { scrollHeight, scrollTop, clientHeight } = event.currentTarget
|
|
93
|
+
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && hasNextPage) fetchNextPage()
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
renderBottomToolbarCustomActions: () => <Typography>Fetched {flatRows.length} of {totalRowCount} rows</Typography>,
|
|
97
|
+
state: { isLoading, showProgressBars: isFetching },
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Virtualization keeps the DOM small while the fetched array grows. Server-side sorting and filtering options are covered in `state-and-server-data`.
|
|
102
|
+
|
|
103
|
+
### Headless virtualization
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { useMRT_Rows, useMRT_RowVirtualizer, useMRT_ColumnVirtualizer } from '@mini_7/material-react-table'
|
|
107
|
+
|
|
108
|
+
const rows = useMRT_Rows(table)
|
|
109
|
+
const rowVirtualizer = useMRT_RowVirtualizer(table)
|
|
110
|
+
const columnVirtualizer = useMRT_ColumnVirtualizer(table)
|
|
111
|
+
|
|
112
|
+
rowVirtualizer?.virtualRows.map((virtualRow) => {
|
|
113
|
+
const row = rows[virtualRow.index]
|
|
114
|
+
return <MyRow key={row.id} row={row} style={{ transform: `translateY(${virtualRow.start}px)` }} />
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The hooks return `undefined` when the matching `enable*Virtualization` option is off. They read the scroll element from `table.refs.tableContainerRef`, so a custom layout must attach that ref to its scroll container.
|
|
119
|
+
|
|
120
|
+
## Common Mistakes
|
|
121
|
+
|
|
122
|
+
### HIGH Enabling virtualization conditionally
|
|
123
|
+
|
|
124
|
+
Wrong:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
enableRowVirtualization: data.length > 100,
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Correct:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
enableRowVirtualization: true, // decide once per table, not per render
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The virtualizer hooks run only when enabled, so toggling the option at runtime changes the hook order and breaks React's rules of hooks; it also flips `layoutMode`, which re-lays out every cell.
|
|
137
|
+
|
|
138
|
+
Source: `docs/guides/virtualization.mdx`
|
|
139
|
+
|
|
140
|
+
### HIGH Leaving pagination on
|
|
141
|
+
|
|
142
|
+
Wrong:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
enableRowVirtualization: true,
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Correct:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
enableRowVirtualization: true,
|
|
152
|
+
enablePagination: false,
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
With pagination on, only one page of rows exists to virtualize, so the table still renders 10 rows and the user pages through 10,000.
|
|
156
|
+
|
|
157
|
+
Source: `docs/guides/virtualization.mdx`
|
|
158
|
+
|
|
159
|
+
### MEDIUM Expecting semantic table CSS to keep working
|
|
160
|
+
|
|
161
|
+
Wrong:
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
enableRowVirtualization: true,
|
|
165
|
+
muiTableBodyCellProps: { sx: { width: '20%' } }, // percentage widths ignored in grid layout
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Correct:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
enableRowVirtualization: true,
|
|
172
|
+
columns: [{ accessorKey: 'email', header: 'Email', size: 300, grow: false }],
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Virtualization forces `layoutMode: 'grid'`, where column widths come from `size`, `minSize`, `maxSize`, and `grow`, not from table-layout CSS.
|
|
176
|
+
|
|
177
|
+
Source: `packages/material-react-table/src/hooks/useMRT_TableOptions.ts`
|
|
178
|
+
|
|
179
|
+
### MEDIUM Detail panels with a fixed row estimate
|
|
180
|
+
|
|
181
|
+
Wrong:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
enableRowVirtualization: true,
|
|
185
|
+
renderDetailPanel: ({ row }) => <Details row={row} />,
|
|
186
|
+
rowVirtualizerOptions: { estimateSize: () => 50 },
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Correct:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
enableRowVirtualization: true,
|
|
193
|
+
renderDetailPanel: ({ row }) => <Details row={row} />,
|
|
194
|
+
// keep MRT's estimate: it counts two virtual items per row and sizes the panel item from the expanded state
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
With a detail panel MRT virtualizes two items per row (row and panel) and estimates the panel at 0 or 100 px depending on expansion. A flat `estimateSize` breaks the scrollbar and item positions.
|
|
198
|
+
|
|
199
|
+
Source: `packages/material-react-table/src/hooks/useMRT_RowVirtualizer.ts`
|
|
200
|
+
|
|
201
|
+
## API Discovery
|
|
202
|
+
|
|
203
|
+
Search `node_modules/@mini_7/material-react-table/dist/index.d.ts` for `Virtualizer` to find `MRT_RowVirtualizer`, `MRT_ColumnVirtualizer`, `MRT_VirtualizerOptions`, and `MRT_VirtualItem`. The TanStack Virtual docs at `https://tanstack.com/virtual/v3/docs/api/virtualizer` list every option and instance method. The Virtualization guide at `/docs/guides/virtualization` and the examples `/docs/examples/virtualized`, `enable-row-virtualization`, `enable-column-virtualization`, `enable-detail-panel-virtualized`, and `infinite-scrolling` show the patterns above.
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
export const CATEGORIES = [
|
|
5
|
+
'tableOptions',
|
|
6
|
+
'columnOptions',
|
|
7
|
+
'stateOptions',
|
|
8
|
+
'tableInstanceAPIs',
|
|
9
|
+
'columnInstanceAPIs',
|
|
10
|
+
'rowInstanceAPIs',
|
|
11
|
+
'cellInstanceAPIs',
|
|
12
|
+
];
|
|
13
|
+
const dataDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'data');
|
|
14
|
+
export const loadApi = () => JSON.parse(readFileSync(join(dataDir, 'api.json'), 'utf8'));
|
|
15
|
+
export const loadDocsIndex = () => JSON.parse(readFileSync(join(dataDir, 'index.json'), 'utf8'));
|
|
16
|
+
export const loadMigrationGuide = () => readFileSync(join(dataDir, 'MIGRATION.md'), 'utf8');
|
|
17
|
+
export const listSkills = () => readdirSync(join(dataDir, 'skills'), { withFileTypes: true })
|
|
18
|
+
.filter((entry) => entry.isDirectory())
|
|
19
|
+
.map((entry) => entry.name)
|
|
20
|
+
.sort();
|
|
21
|
+
export const loadSkill = (name) => {
|
|
22
|
+
if (!listSkills().includes(name))
|
|
23
|
+
return undefined;
|
|
24
|
+
return readFileSync(join(dataDir, 'skills', name, 'SKILL.md'), 'utf8');
|
|
25
|
+
};
|
|
26
|
+
const safeName = /^[a-z0-9-]+$/;
|
|
27
|
+
export const loadGuide = (name) => {
|
|
28
|
+
const file = join(dataDir, 'guides', `${name}.md`);
|
|
29
|
+
if (!safeName.test(name) || !existsSync(file))
|
|
30
|
+
return undefined;
|
|
31
|
+
return readFileSync(file, 'utf8');
|
|
32
|
+
};
|
|
33
|
+
export const loadReference = (name) => {
|
|
34
|
+
const file = join(dataDir, 'reference', `${name}.md`);
|
|
35
|
+
if (!safeName.test(name) || !existsSync(file))
|
|
36
|
+
return undefined;
|
|
37
|
+
return readFileSync(file, 'utf8');
|
|
38
|
+
};
|
|
39
|
+
export const loadExample = (id) => {
|
|
40
|
+
const file = join(dataDir, 'examples', `${id}.tsx`);
|
|
41
|
+
if (!safeName.test(id) || !existsSync(file))
|
|
42
|
+
return undefined;
|
|
43
|
+
return readFileSync(file, 'utf8');
|
|
44
|
+
};
|
|
45
|
+
const tokenize = (query) => query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
46
|
+
/** Ranks name matches above description matches; exact and prefix matches first. */
|
|
47
|
+
export const searchApi = (api, query, categories = CATEGORIES, limit = 20) => {
|
|
48
|
+
const terms = tokenize(query);
|
|
49
|
+
if (terms.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
const hits = [];
|
|
52
|
+
for (const category of categories) {
|
|
53
|
+
for (const entry of api.categories[category]) {
|
|
54
|
+
const name = entry.name.toLowerCase();
|
|
55
|
+
const description = entry.description.toLowerCase();
|
|
56
|
+
let score = 0;
|
|
57
|
+
for (const term of terms) {
|
|
58
|
+
if (name === term)
|
|
59
|
+
score += 100;
|
|
60
|
+
else if (name.startsWith(term))
|
|
61
|
+
score += 60;
|
|
62
|
+
else if (name.includes(term))
|
|
63
|
+
score += 40;
|
|
64
|
+
else if (description.includes(term))
|
|
65
|
+
score += 10;
|
|
66
|
+
else {
|
|
67
|
+
score = 0;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (score > 0)
|
|
72
|
+
hits.push({ ...entry, category, score });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return hits
|
|
76
|
+
.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
|
|
77
|
+
.slice(0, limit);
|
|
78
|
+
};
|
|
79
|
+
/** Every term must match the name, title, headings, or description of a document. */
|
|
80
|
+
export const searchDocs = (index, query, limit = 20, kinds = ['guide', 'reference', 'skill', 'example']) => {
|
|
81
|
+
const terms = tokenize(query);
|
|
82
|
+
if (terms.length === 0)
|
|
83
|
+
return [];
|
|
84
|
+
const docs = [];
|
|
85
|
+
const pushDoc = (kind, entry) => docs.push({
|
|
86
|
+
kind,
|
|
87
|
+
name: entry.name,
|
|
88
|
+
summary: entry.description || entry.title,
|
|
89
|
+
text: [entry.title, ...entry.headings, entry.description]
|
|
90
|
+
.join(' ')
|
|
91
|
+
.toLowerCase(),
|
|
92
|
+
});
|
|
93
|
+
index.guides.forEach((entry) => pushDoc('guide', entry));
|
|
94
|
+
index.reference.forEach((entry) => pushDoc('reference', entry));
|
|
95
|
+
index.skills.forEach((entry) => pushDoc('skill', entry));
|
|
96
|
+
index.examples.forEach((entry) => docs.push({
|
|
97
|
+
kind: 'example',
|
|
98
|
+
name: entry.id,
|
|
99
|
+
summary: entry.guides.length
|
|
100
|
+
? `used in guides: ${entry.guides.join(', ')}`
|
|
101
|
+
: entry.pages.length
|
|
102
|
+
? `shown on example pages: ${entry.pages.join(', ')}`
|
|
103
|
+
: 'standalone example',
|
|
104
|
+
text: [...entry.guides, ...entry.pages].join(' ').toLowerCase(),
|
|
105
|
+
}));
|
|
106
|
+
const hits = [];
|
|
107
|
+
for (const doc of docs) {
|
|
108
|
+
if (!kinds.includes(doc.kind))
|
|
109
|
+
continue;
|
|
110
|
+
const name = doc.name.toLowerCase();
|
|
111
|
+
let score = 0;
|
|
112
|
+
for (const term of terms) {
|
|
113
|
+
if (name === term)
|
|
114
|
+
score += 100;
|
|
115
|
+
else if (name.split('-').includes(term))
|
|
116
|
+
score += 60;
|
|
117
|
+
else if (name.includes(term))
|
|
118
|
+
score += 40;
|
|
119
|
+
else if (doc.text.includes(term))
|
|
120
|
+
score += 10;
|
|
121
|
+
else {
|
|
122
|
+
score = 0;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (score > 0)
|
|
127
|
+
hits.push({
|
|
128
|
+
kind: doc.kind,
|
|
129
|
+
name: doc.name,
|
|
130
|
+
summary: doc.summary,
|
|
131
|
+
score,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return hits
|
|
135
|
+
.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
|
|
136
|
+
.slice(0, limit);
|
|
137
|
+
};
|