@jielga/tmdatagrid 2.0.0-beta.2 → 2.0.0-beta.21

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 (150) hide show
  1. package/README.md +5 -212
  2. package/dist/index.d.ts +1664 -632
  3. package/dist/index.js +5226 -3223
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/docs/anatomy.md +102 -0
  7. package/docs/cell-selection.md +154 -0
  8. package/docs/column-layout.md +204 -0
  9. package/docs/columns.md +262 -0
  10. package/docs/components.md +304 -0
  11. package/docs/editing.md +603 -0
  12. package/docs/editors.md +250 -0
  13. package/docs/export.md +326 -0
  14. package/docs/filtering.md +358 -0
  15. package/docs/getting-started.md +123 -0
  16. package/docs/grouping.md +165 -0
  17. package/docs/loading-and-empty.md +92 -0
  18. package/docs/localization.md +79 -0
  19. package/docs/menu.md +143 -0
  20. package/docs/pagination.md +144 -0
  21. package/docs/persistence.md +111 -0
  22. package/docs/portfolio-rebalancer.md +94 -0
  23. package/docs/query-builder.md +175 -0
  24. package/docs/quick-search.md +83 -0
  25. package/docs/row-details.md +113 -0
  26. package/docs/row-interaction.md +148 -0
  27. package/docs/row-pinning.md +132 -0
  28. package/docs/row-selection.md +134 -0
  29. package/docs/row-styling.md +133 -0
  30. package/docs/scrolling.md +111 -0
  31. package/docs/server-query.md +246 -0
  32. package/docs/server-side.md +206 -0
  33. package/docs/sorting.md +101 -0
  34. package/docs/styling.md +126 -0
  35. package/docs/summary-row.md +76 -0
  36. package/docs/testing.md +309 -0
  37. package/docs/toolbar.md +161 -0
  38. package/docs/use-tm-data-grid.md +361 -0
  39. package/package.json +21 -45
  40. package/skills/appearance/SKILL.md +70 -17
  41. package/skills/cell-selection/SKILL.md +70 -76
  42. package/skills/columns/SKILL.md +131 -32
  43. package/skills/data/SKILL.md +100 -23
  44. package/skills/editing/SKILL.md +217 -96
  45. package/skills/editing/references/common-mistakes.md +111 -24
  46. package/skills/editing/references/editing-api.md +63 -39
  47. package/skills/editing/references/editors-and-validation.md +77 -19
  48. package/skills/filtering/SKILL.md +148 -40
  49. package/skills/getting-started/SKILL.md +18 -16
  50. package/skills/grouping/SKILL.md +32 -15
  51. package/skills/options/SKILL.md +39 -9
  52. package/skills/rows/SKILL.md +22 -18
  53. package/skills/server-side/SKILL.md +170 -17
  54. package/skills/testing/SKILL.md +10 -7
  55. package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +7 -19
  56. package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +7 -1
  57. package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +39 -23
  58. package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +106 -38
  59. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
  60. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
  61. package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +4 -4
  62. package/src/components/TMDataGridDraftActions.tsx +307 -0
  63. package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +58 -50
  64. package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +150 -115
  65. package/src/components/TMDataGridExportPicker.module.css +77 -0
  66. package/src/components/TMDataGridExportPicker.tsx +234 -0
  67. package/src/components/TMDataGridFilterPanel.module.css +54 -0
  68. package/src/components/TMDataGridFilterPanel.tsx +348 -0
  69. package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +7 -5
  70. package/src/components/TMDataGridFilterSurface.module.css +54 -0
  71. package/src/components/TMDataGridFilterSurface.tsx +167 -0
  72. package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -13
  73. package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +4 -3
  74. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +10 -0
  75. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +100 -28
  76. package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
  77. package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
  78. package/src/components/TMDataGridMenu.tsx +354 -0
  79. package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +12 -7
  80. package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +90 -67
  81. package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +678 -156
  82. package/src/components/TMDataGridToolbar.module.css +21 -0
  83. package/src/components/TMDataGridToolbar.tsx +181 -0
  84. package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
  85. package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
  86. package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
  87. package/src/components/editors/TMDataGridNumberEditor.tsx +70 -0
  88. package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
  89. package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
  90. package/src/{tmdatagrid/components → components}/editors/editorShared.ts +17 -31
  91. package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
  92. package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
  93. package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
  94. package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
  95. package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
  96. package/src/components/filters/controlLayout.ts +32 -0
  97. package/src/components/filters/filterControlFor.ts +65 -0
  98. package/src/{tmdatagrid/components → components}/icons.ts +1 -0
  99. package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
  100. package/src/components/useHideableColumns.ts +52 -0
  101. package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
  102. package/src/{tmdatagrid/core → core}/capabilities.ts +14 -6
  103. package/src/{tmdatagrid/core → core}/columnOptions.ts +46 -0
  104. package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
  105. package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
  106. package/src/core/controlledState.ts +179 -0
  107. package/src/core/controlledStateSync.ts +108 -0
  108. package/src/core/deletedRows.ts +34 -0
  109. package/src/core/dom.ts +74 -0
  110. package/src/core/editEngine.ts +2476 -0
  111. package/src/{tmdatagrid/core → core}/editorFocus.ts +8 -4
  112. package/src/core/export.ts +843 -0
  113. package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
  114. package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
  115. package/src/core/filterSurface.ts +99 -0
  116. package/src/{tmdatagrid/core → core}/labels.ts +66 -8
  117. package/src/{tmdatagrid/core → core}/labelsSv.ts +26 -3
  118. package/src/core/pageReset.ts +120 -0
  119. package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
  120. package/src/core/resizePreview.ts +141 -0
  121. package/src/core/summary.ts +59 -0
  122. package/src/core/useSettledTableState.ts +36 -0
  123. package/src/{tmdatagrid/index.ts → index.ts} +75 -12
  124. package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +734 -135
  125. package/src/useTMDataGridExport.ts +78 -0
  126. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +0 -162
  127. package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
  128. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
  129. package/src/tmdatagrid/components/TMDataGridToolbar.module.css +0 -12
  130. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -162
  131. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +0 -40
  132. package/src/tmdatagrid/core/cellExport.ts +0 -320
  133. package/src/tmdatagrid/core/editEngine.ts +0 -1006
  134. package/src/tmdatagrid/core/summary.ts +0 -35
  135. /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
  136. /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
  137. /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
  138. /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
  139. /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
  140. /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
  141. /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
  142. /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
  143. /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
  144. /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
  145. /package/src/{tmdatagrid/core → core}/grouping.ts +0 -0
  146. /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
  147. /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
  148. /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
  149. /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
  150. /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
@@ -0,0 +1,206 @@
1
+ # Server-side data
2
+
3
+ When the client holds one page and the server does the work, paging, sorting and
4
+ filtering all become round trips and the grid stops doing them itself.
5
+
6
+ The grid reads rows through `getPaginatedRowModel()` and totals through
7
+ `getRowCount()` and `getPageCount()`, all of which respect TanStack's manual
8
+ modes. A server-driven grid therefore requires only the standard `manual*`
9
+ configuration. `manualPagination: true` also switches the grid's pagination flag
10
+ on, so `TMDataGrid.Footer` renders its pager without `enablePagination`.
11
+
12
+ When the total is unknown, declare `pageCount: -1`: the next button stays
13
+ enabled and the `pagination` render prop receives `pageCount: -1`.
14
+
15
+ ## Usage
16
+
17
+ ```tsx
18
+ const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 25 });
19
+ const [sorting, setSorting] = useState([]);
20
+ const [columnFilters, setColumnFilters] = useState([]);
21
+
22
+ const { data, isFetching } = useQuery({
23
+ queryKey: ["employees", pagination, sorting, columnFilters],
24
+ queryFn: () => fetchEmployees({ pagination, sorting, columnFilters }),
25
+ placeholderData: keepPreviousData,
26
+ });
27
+
28
+ const grid = useTMDataGrid({
29
+ columns,
30
+ data: data?.rows ?? [],
31
+ getRowId: (row) => String(row.id),
32
+
33
+ manualPagination: true,
34
+ manualSorting: true,
35
+ manualFiltering: true,
36
+ rowCount: data?.total ?? 0,
37
+
38
+ state: { pagination, sorting, columnFilters },
39
+ onPaginationChange: setPagination,
40
+ onSortingChange: setSorting,
41
+ onColumnFiltersChange: setColumnFilters,
42
+
43
+ meta: { loading: isFetching, totalRowCount: data?.totalUnfiltered },
44
+ });
45
+ ```
46
+
47
+ ```demo
48
+ file: data/ServerSide.tsx
49
+ hint: Sorting, searching and paging are all round trips against a server with 500 ms of latency.
50
+ extraSources: data/orders.ts
51
+ ```
52
+
53
+ ## Differences from client-side data
54
+
55
+ | Concern | Client-side | Server-side |
56
+ | --- | --- | --- |
57
+ | Rendered rows | The current page, sliced locally | The rows returned by the server |
58
+ | Footer total | Pre-paginated row count | `options.rowCount` |
59
+ | `SummaryCount` total | Pre-filtered row count | `meta.totalRowCount`; without it, the count renders alone |
60
+ | Loading state | Not applicable | `meta.loading` |
61
+
62
+ No additional configuration is required. Column menus, the filter panel and the
63
+ column manager behave identically in both modes.
64
+
65
+ ## The page index
66
+
67
+ A column filter, the quick search or a sort changes what page 3 means.
68
+ The result set is a different one, and it may not have a page 3 at all - so the grid resets `pageIndex` to 0 whenever the query changes, on every grid.
69
+ The reset is applied in the same event as the change, so one request goes out, for the first page of the new query.
70
+
71
+ `resetPageOnQueryChange: false` switches it off.
72
+
73
+ TanStack's `autoResetPageIndex` is a different rule and does not cover this.
74
+ It fires on a change to `data` - which server-side is the response landing, after the request was sent, and under `editing.draft` every commit - so the grid switches it off everywhere and resets on the query change itself.
75
+
76
+ ## Column options
77
+
78
+ `meta.options: "faceted"` reads the distinct values present in `data`, which server-side is one page of them.
79
+ The dropdown then offers whatever happened to be on the page the user is looking at, and looks correct while being wrong.
80
+ Declare the set instead, as a list or a function.
81
+ The grid warns once per column when a faceted column resolves under `manualFiltering` or `manualPagination`.
82
+
83
+ ## Sending filters
84
+
85
+ Filter values are plain JSON:
86
+
87
+ ```json
88
+ [
89
+ { "id": "lastName", "value": { "operator": "contains", "value": "holm" } },
90
+ { "id": "age", "value": { "operator": "greaterThan", "value": "30" } },
91
+ { "id": "status", "value": { "operator": "isAnyOf", "value": ["Paid", "Pending"] } },
92
+ { "id": "hired", "value": { "operator": "onOrAfter", "value": "2026-01-01" } }
93
+ ]
94
+ ```
95
+
96
+ Forward `columnFilters` unchanged and translate it at the API boundary.
97
+ `activeColumnFilters` hands back the entries that are narrowing the grid, typed:
98
+ `ColumnFiltersState` types `value` as `unknown`, and an entry whose value is
99
+ still empty matches every row.
100
+
101
+ ```ts
102
+ import { activeColumnFilters } from "@jielga/tmdatagrid";
103
+
104
+ const active = activeColumnFilters(columnFilters);
105
+ // [{ id: "lastName", value: { operator: "contains", value: "holm" } }]
106
+ ```
107
+
108
+ It takes the `columnFilters` array, or the table where the grid owns the slice.
109
+ `isFilterActive(value)` is the single-value test it is built on.
110
+ Only the grid's own `{ operator, value }` shape is read: an entry holding some
111
+ other value - a custom filter control writing raw values - is dropped.
112
+
113
+ Debounce requests. The filter value input updates on every keystroke.
114
+
115
+ An endpoint that answers only some operators - `contains` and `equals` but no
116
+ `startsWith` - should not have the rest offered. `meta.filter.operators` on the
117
+ column narrows the panel and the header funnel to the operators the query can
118
+ express; see [Operators](/docs/filtering#operators).
119
+
120
+ For an endpoint that speaks its own query language rather than taking the
121
+ grid's filter model, see [A server-backed search](/docs/server-query): one
122
+ mapping layer turning filters, sorting and the page index into a request body,
123
+ and the response envelope back into rows.
124
+
125
+ ## Persistence
126
+
127
+ `persist` works unchanged. `dataKey` restores filters, sorting and pagination
128
+ before the first request, so reloading the page repeats the query the user last
129
+ ran.
130
+
131
+ If the same state is also held in your own `useState`, initialise it from the
132
+ same source or let the grid own it. Do not maintain both independently.
133
+
134
+ ## Row selection
135
+
136
+ Row selection is keyed by `getRowId`, so ids must be stable across pages. With
137
+ `manualPagination`, rows selected on an earlier page remain in `rowSelection`
138
+ even though they are no longer mounted. Read the state rather than the row
139
+ models:
140
+
141
+ ```ts
142
+ const selectedIds = Object.keys(grid.table.store.state.rowSelection);
143
+ ```
144
+
145
+ ## Infinite scroll
146
+
147
+ An alternative to the pager: keep every fetched row in `data` and load more as
148
+ the user scrolls. `onReachEnd` on `TMDataGrid.Table` fires as the scroll nears
149
+ the last row. Append the next page and the virtualizer keeps its position:
150
+
151
+ ```tsx
152
+ const [rows, setRows] = useState<Order[]>([]);
153
+
154
+ const grid = useTMDataGrid({
155
+ data: rows,
156
+ columns,
157
+ getRowId: (row) => String(row.id),
158
+ meta: { loading: isFetching, totalRowCount: total },
159
+ enableSorting: false,
160
+ enableColumnFilters: false,
161
+ });
162
+
163
+ <TMDataGrid.Table onReachEnd={() => void fetchNextPage()} />
164
+ ```
165
+
166
+ ```demo
167
+ file: data/InfiniteScroll.tsx
168
+ hint: Scroll to the bottom and keep going. 100 rows arrive at a time and the scroll position holds.
169
+ extraSources: data/orders.ts
170
+ height: 460
171
+ ```
172
+
173
+ Fetch page zero yourself on mount. `onReachEnd` does not fire on an empty grid.
174
+
175
+ `onReachEnd` fires once per row count, so a pending fetch is not requested again
176
+ until its rows land. `reachEndThreshold` (default 10) sets how many rows before
177
+ the end it fires. `TMDataGrid.LoadingIndicator` in the toolbar shows the fetch,
178
+ since the body keeps showing the rows it already has.
179
+
180
+ Two constraints apply:
181
+
182
+ - **Sorting and filtering must be server-side** (`manualSorting` /
183
+ `manualFiltering`) or disabled. The client only holds a prefix of the data, so
184
+ a client-side sort would order that prefix and present it as the whole. When a
185
+ server-side sort or filter changes, reset the accumulated rows and start from
186
+ page zero.
187
+ - **Not compatible with `enablePagination`.** The pager slices the same scroll
188
+ the callback watches, so the end reached is the page's rather than the data's.
189
+ The grid warns once if both are set.
190
+
191
+ ## Reference
192
+
193
+ | Name | Kind | Type | Default | What it does |
194
+ | --- | --- | --- | --- | --- |
195
+ | `manualPagination` | Table option | `boolean` | `false` | The server pages. Implies `enablePagination`. |
196
+ | `manualSorting` | Table option | `boolean` | `false` | The server sorts. |
197
+ | `manualFiltering` | Table option | `boolean` | `false` | The server filters, column filters and quick search alike. |
198
+ | `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. See [Grouping](/docs/grouping#server-side-grids). |
199
+ | `rowCount` | Table option | `number` | – | The true total. `pageCount: -1` when it is unknown. |
200
+ | `meta.loading` | Option | `boolean` | `false` | A fetch is in flight. See [Loading and empty states](/docs/loading-and-empty). |
201
+ | `meta.totalRowCount` | Option | `number` | – | The unfiltered total, for `SummaryCount`. |
202
+ | `resetPageOnQueryChange` | Option | `boolean` | `true` | Back to page 1 when a filter, the quick search, the sort or the grouping changes. |
203
+ | `onReachEnd` | Table prop | `() => void` | – | Fires as the scroll nears the last row. Latches per row count. |
204
+ | `reachEndThreshold` | Table prop | `number` | `10` | How many rows before the end it fires. |
205
+ | `activeColumnFilters` | Export | `(columnFilters \| table) => Array<{ id, value }>` | – | The filters in the grid's own value shape that narrow anything, typed. |
206
+ | `isFilterActive` | Export | `(value) => boolean` | – | Whether one filter value narrows anything. |
@@ -0,0 +1,101 @@
1
+ # Sorting
2
+
3
+ On by default. Click a header to sort it, click again to reverse, click a third
4
+ time to clear. The column menu has the same three actions.
5
+
6
+ ```tsx
7
+ const grid = useTMDataGrid({ data, columns });
8
+ ```
9
+
10
+ ```demo
11
+ file: columns/Sorting.tsx
12
+ hint: Click a header to sort, Shift+click a second to append - the badge beside the arrow is its priority.
13
+ ```
14
+
15
+ ## Sorting by more than one column
16
+
17
+ Shift+click a second header to **add** it to the sort rather than replace it.
18
+ While more than one column sorts, each sorted header shows its priority - 1, 2,
19
+ … - beside the arrow, so the order they are applied in is visible.
20
+
21
+ A plain click still replaces the whole sort, and the menu's Sort items do the
22
+ same.
23
+
24
+ This is TanStack's own `isMultiSortEvent`, so `enableMultiSort`,
25
+ `maxMultiSortColCount` and a custom `isMultiSortEvent` all pass straight
26
+ through:
27
+
28
+ ```tsx
29
+ const grid = useTMDataGrid({
30
+ data,
31
+ columns,
32
+ maxMultiSortColCount: 3,
33
+ // Ctrl rather than Shift, say.
34
+ isMultiSortEvent: (event) => event.ctrlKey,
35
+ });
36
+ ```
37
+
38
+ ## Turning it off
39
+
40
+ `enableSorting: false` on the table removes click-to-sort, the indicator and
41
+ the menu items everywhere; on a column it removes them for that column alone.
42
+
43
+ ```tsx
44
+ columnHelper.accessor("avatar", { header: "", enableSorting: false });
45
+ ```
46
+
47
+ A column whose menu has no remaining items renders no menu button and does not
48
+ handle right-click, so the browser's own menu opens instead.
49
+
50
+ ## Where the state lives
51
+
52
+ Sorting writes TanStack's `sorting` state, an array of `{ id, desc }` in
53
+ priority order. Seed it, control it, or read it like any other slice:
54
+
55
+ ```tsx
56
+ const grid = useTMDataGrid({
57
+ data,
58
+ columns,
59
+ initialState: { sorting: [{ id: "lastName", desc: false }] },
60
+ });
61
+ ```
62
+
63
+ It is a **data** slice: it names a column and a direction over the data itself,
64
+ so a [persisted](/docs/use-tm-data-grid#persist) grid comes back sorted the way
65
+ it was left, under `dataKey`. For a server that does the sorting, see
66
+ [Server-side data](/docs/server-side).
67
+
68
+ Grouping runs first, so a grouped grid sorts rows within each group and orders
69
+ the groups by their aggregated value. See
70
+ [Grouping](/docs/grouping#sorting-a-grouped-grid).
71
+
72
+ ## Custom comparators
73
+
74
+ `sortFn` on the column takes any of TanStack's registered names, or a function
75
+ of two rows and the column id. It is `sortFn` in v9, not v8's `sortingFn`.
76
+
77
+ ```tsx
78
+ columnHelper.accessor("priority", {
79
+ header: "Priority",
80
+ sortFn: (rowA, rowB) =>
81
+ RANK[rowA.original.priority] - RANK[rowB.original.priority],
82
+ });
83
+ ```
84
+
85
+ ## The header menu
86
+
87
+ The menu opens from the ⋮ button on the header, or from a right-click anywhere
88
+ on it. The items are the same either way; a right-click opens it at the
89
+ pointer.
90
+
91
+ ## Reference
92
+
93
+ | Name | Kind | Type | Default | What it does |
94
+ | --- | --- | --- | --- | --- |
95
+ | `enableSorting` | Table option | `boolean` | `true` | Also a column option. `false` removes indicator, menu items and click-to-sort. |
96
+ | `enableMultiSort` | Table option | `boolean` | `true` | Whether Shift+click appends instead of replacing. |
97
+ | `maxMultiSortColCount` | Table option | `number` | `Infinity` | How many columns may sort at once. |
98
+ | `isMultiSortEvent` | Table option | `(event) => boolean` | Shift held | What counts as "append to the sort". |
99
+ | `sortFn` | Column option | name \| `(rowA, rowB, columnId) => number` | `"auto"` | The comparator for one column. v9's name for v8's `sortingFn`. |
100
+ | `initialState.sorting` | Table option | `Array<{ id, desc }>` | `[]` | Sort at mount. A settings slice, so it persists. |
101
+ | `manualSorting` | Table option | `boolean` | `false` | The server sorts; the grid stops. |
@@ -0,0 +1,126 @@
1
+ # Size, styling and theming
2
+
3
+ The grid is themed through CSS custom properties, and sized through Mantine's
4
+ standard `size` scale. Both are set on the root element, so a grid can be
5
+ themed per instance without a provider.
6
+
7
+ ```tsx
8
+ <TMDataGrid {...grid} size="sm" style={{ "--dg-row-selected-bg": "color-mix(in srgb, var(--mantine-color-blue-6) 12%, transparent)" }} />
9
+ ```
10
+
11
+ ```demo
12
+ file: customization/Styling.tsx
13
+ hint: Every value the controls change is a CSS variable set on the grid element.
14
+ extraSources: data/employeeColumns.tsx
15
+ ```
16
+
17
+ ## The size scale
18
+
19
+ `size` drives row height, header height, font size and cell padding together,
20
+ and selects the size of every Mantine control the grid renders - the page-size
21
+ select, the filter inputs, the column checkboxes.
22
+
23
+ | `size` | Row height | Header height | Font size | Cell padding |
24
+ | --- | --- | --- | --- | --- |
25
+ | `xs` | 34px | 32px | `xs` | 6px |
26
+ | `sm` | 42px | 38px | `sm` | 8px |
27
+ | `md` (default) | 52px | 44px | `sm` | 10px |
28
+ | `lg` | 62px | 52px | `md` | 14px |
29
+ | `xl` | 72px | 60px | `lg` | 18px |
30
+
31
+ ```demo
32
+ file: getting-started/DensityAndLayout.tsx
33
+ ```
34
+
35
+ Row height is also required by the virtualizer **as a number**, so it cannot be
36
+ defined in CSS alone. `SIZE_ROW_HEIGHT` is the exported source of these values,
37
+ and the stylesheet mirrors them. To use a height outside the scale, set
38
+ `meta.rowHeight` rather than the variable.
39
+
40
+ ## CSS variables
41
+
42
+ `style` accepts custom properties, and `className` reaches the same element
43
+ from a stylesheet.
44
+ The wrapper components - `Toolbar`, `Spacer`, `Footer`, `FilterPanel`, `FilterPills` and `ColumnsPanel` - take Mantine's `BoxProps` on top of their own props, so `mb="sm"` or `hiddenFrom="sm"` on any of them sets the element itself; see [Toolbar](/docs/toolbar#style-props).
45
+
46
+ ### Metrics
47
+
48
+ | Variable | Default | Applies to |
49
+ | --- | --- | --- |
50
+ | `--dg-row-height` | From `size` | Row height. Prefer `meta.rowHeight` - the virtualizer needs the number. |
51
+ | `--dg-header-height` | From `size` | Header row height |
52
+ | `--dg-summary-height` | From `size` | [Summary row](/docs/summary-row) height |
53
+ | `--dg-entry-height` | From `size` | The sticky [entry block](/docs/editing#adding-and-deleting-rows) |
54
+ | `--dg-font-size` | From `size` | Cell and header font size |
55
+ | `--dg-padding` | From `size` | Horizontal cell padding. The generated lanes are excluded: they are fixed 36px tracks that centre their control. |
56
+ | `--dg-radius` | `--mantine-radius-md` | The frame's corner radius. `0` squares the grid off. The root clips its overflow, so the header and the last row follow it. |
57
+
58
+ ### Colours
59
+
60
+ Both colour schemes are supported. The grid's own stylesheet resolves its
61
+ colours with `light-dark()`, so it follows Mantine's scheme without a prop or a
62
+ second import. A default of **Themed** below means exactly that: the variable
63
+ resolves to one value under the light scheme and another under the dark one.
64
+ Set such a variable and you take over both schemes, so give it a value that
65
+ reads in each - `light-dark()` works in your own value too.
66
+
67
+ | Variable | Default | Applies to |
68
+ | --- | --- | --- |
69
+ | `--row-bg` | – | One row's own background. Set this, never `background`. See [Row styling](/docs/row-styling#set-the-row-background). |
70
+ | `--dg-row-selected-bg` | `--mantine-primary-color-light` | [Selected](/docs/row-selection) rows |
71
+ | `--dg-row-highlight-bg` | Themed | The highlighted row |
72
+ | `--dg-row-striped-bg` | Themed | Every second row under `striped` |
73
+ | `--dg-row-group-bg` | Themed | [Group](/docs/grouping) rows |
74
+ | `--dg-row-new-bg` | Green tint | New rows entered into the [draft store](/docs/editing#the-draft-store) |
75
+ | `--dg-match-highlight-bg` | Themed yellow | [Marked](/docs/quick-search#match-highlighting) text |
76
+ | `--dg-header-shadow-color` | Themed | The shadow under the sticky header |
77
+
78
+ ### Layout internals
79
+
80
+ | Variable | Default | Applies to |
81
+ | --- | --- | --- |
82
+ | `--dg-sticky-edge-range` | `20px` | How far the pinned-lane band takes to fade in |
83
+ | `--dg-edge-top` · `-bottom` · `-left` · `-right` | – | Set by the grid to mark [cell-range](/docs/cell-selection) borders |
84
+ | `--dg-z-header` · `-pinned-cell` · `-summary-row` · … | – | The stacking order. Change these only to place something of your own between two layers. |
85
+
86
+ ## The stylesheet
87
+
88
+ One import, once, anywhere in your app:
89
+
90
+ ```tsx
91
+ import "@jielga/tmdatagrid/styles.css";
92
+ ```
93
+
94
+ `@jielga/tmdatagrid/styles.layer.css` is the same stylesheet wrapped in a
95
+ `@layer`, for an application that orders its own layers and needs the grid to
96
+ sit at a known place in that order. Import **one** of the two, never both.
97
+
98
+ ## Layout
99
+
100
+ The grid fills the box you give it and scrolls inside it. It does not size
101
+ itself to its content: a virtualized grid has no content height to measure.
102
+
103
+ ```tsx
104
+ <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
105
+ <TMDataGrid {...grid} style={{ flex: 1, minHeight: 0 }}>
106
+ <TMDataGrid.Table />
107
+ </TMDataGrid>
108
+ </div>
109
+ ```
110
+
111
+ `minHeight: 0` is required. A flex item's default `min-height: auto` will not
112
+ shrink below its content, so without it the grid grows past the viewport instead
113
+ of scrolling.
114
+
115
+ ## Reference
116
+
117
+ | Name | Kind | Type | Default | What it does |
118
+ | --- | --- | --- | --- | --- |
119
+ | `size` | Prop | `MantineSize` | `"md"` | The whole density scale. |
120
+ | `className` | Prop | `string` | – | Added to the root element's classes. |
121
+ | `style` | Prop | `CSSProperties` + `--*` | – | Root element styles, including the variables above. |
122
+ | `id` | Prop | `string` | – | Set on the root element. |
123
+ | `meta.rowHeight` | Option | `number` | From `size` | A row height outside the scale. |
124
+ | `SIZE_ROW_HEIGHT` | Export | `Record<MantineSize, number>` | – | The row heights listed in the table above. |
125
+ | `SIZE_CONTROL_SIZE` | Export | `Record<MantineSize, MantineSize>` | – | Which control size each grid size uses. |
126
+ | `DEFAULT_TMDATAGRID_SIZE` | Export | `"md"` | – | The default. |
@@ -0,0 +1,76 @@
1
+ # Summary row
2
+
3
+ A sticky row along the bottom edge holding totals for the whole grid: a count,
4
+ a sum, or any other single number per column. It is independent of
5
+ [grouping](/docs/grouping), which totals each category instead of everything.
6
+
7
+ There is no flag. Give a column a `footer` and the row appears. The row exists
8
+ whenever at least one visible column defines one.
9
+
10
+ ```tsx
11
+ columnHelper.accessor("salary", {
12
+ header: "Salary",
13
+ footer: ({ table }) =>
14
+ sek(Number(aggregateColumn({ table, columnId: "salary" }))),
15
+ });
16
+ ```
17
+
18
+ ```demo
19
+ file: rows/SummaryRow.tsx
20
+ ```
21
+
22
+ `footer` is TanStack's own column option, rendered the way the header is: each
23
+ cell renders that column's renderer with the header context. It can render
24
+ anything: a static label, a count, or its own calculation.
25
+
26
+ ## Totalling a column
27
+
28
+ `aggregateColumn({ table, columnId, fn })` computes over every **filtered** row
29
+ (all pages, following the filters live) through the registered aggregation
30
+ functions. `fn` defaults to `"sum"`.
31
+
32
+ Every data row counts once.
33
+ Grouping builds its group rows from this model rather than into it, so a grouped grid totals its records and not its records plus their subtotals.
34
+ A tree built with `getSubRows` counts parents and children alike.
35
+
36
+ It totals `data`, so under [`editing.draft`](/docs/editing) a committed edit is not in the total until `edit.saveDrafts()` sends it and the new `data` arrives.
37
+ An edited cell shows its draft and the summary row does not follow it.
38
+ The same holds for a group row's `aggregatedCell`: no group row has a form, so aggregates read the committed values throughout.
39
+
40
+ ```tsx
41
+ aggregateColumn({ table, columnId: "salary" }); // sum
42
+ aggregateColumn({ table, columnId: "age", fn: "mean" }); // average
43
+ aggregateColumn({ table, columnId: "location", fn: "uniqueCount" });
44
+ ```
45
+
46
+ It takes `table`, so the same total can be read anywhere the table is in
47
+ reach - a toolbar readout as much as a `footer`:
48
+
49
+ ```tsx
50
+ const { table } = useTMDataGrid({ data, columns });
51
+
52
+ <TMDataGrid.Toolbar>
53
+ <Text size="xs">
54
+ Payroll {sek(Number(aggregateColumn({ table, columnId: "salary" })))}
55
+ </Text>
56
+ </TMDataGrid.Toolbar>;
57
+ ```
58
+
59
+ ## Layout
60
+
61
+ Pinned columns keep their lanes in the summary row, and the row sits under the
62
+ pinned-lane gradients in the stacking order. The generated lanes - checkbox,
63
+ tree, details, row numbers - define no `footer`, so their summary cells stay
64
+ blank.
65
+
66
+ The row is sticky, so it stays put while the body scrolls, and its height is
67
+ `--dg-summary-height`.
68
+
69
+ ## Reference
70
+
71
+ | Name | Kind | Type | Default | What it does |
72
+ | --- | --- | --- | --- | --- |
73
+ | `footer` | Column option | `(ctx) => ReactNode` | – | Renders this column's summary cell. Defining one on any column adds the row. |
74
+ | `aggregateColumn` | Export | `({ table, columnId, fn }) => unknown` | `fn: "sum"` | Aggregates a column over every filtered row, all pages. |
75
+ | `TMDataGridAggregationName` | Export | type | – | The registered function names - `sum`, `mean`, `count`, `uniqueCount`, … |
76
+ | `--dg-summary-height` | CSS variable | length | From `size` | Height of the summary row. |