@jielga/tmdatagrid 2.0.0-beta.9 → 2.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 (157) hide show
  1. package/README.md +5 -212
  2. package/dist/index.d.ts +1281 -768
  3. package/dist/index.js +4607 -3250
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/docs/adding-rows.md +132 -0
  7. package/docs/anatomy.md +119 -0
  8. package/docs/card-view.md +108 -0
  9. package/docs/cell-selection.md +194 -0
  10. package/docs/column-layout.md +182 -0
  11. package/docs/column-menu.md +66 -0
  12. package/docs/columns.md +269 -0
  13. package/docs/components.md +311 -0
  14. package/docs/draft-store.md +242 -0
  15. package/docs/editing.md +303 -0
  16. package/docs/editors.md +250 -0
  17. package/docs/export.md +319 -0
  18. package/docs/filtering.md +362 -0
  19. package/docs/getting-started.md +123 -0
  20. package/docs/grouping.md +165 -0
  21. package/docs/loading-and-empty.md +92 -0
  22. package/docs/localization.md +79 -0
  23. package/docs/menu.md +143 -0
  24. package/docs/migrating-to-2.md +163 -0
  25. package/docs/pagination.md +144 -0
  26. package/docs/persistence.md +114 -0
  27. package/docs/portfolio-rebalancer.md +94 -0
  28. package/docs/query-builder.md +179 -0
  29. package/docs/quick-search.md +84 -0
  30. package/docs/row-details.md +115 -0
  31. package/docs/row-interaction.md +149 -0
  32. package/docs/row-pinning.md +132 -0
  33. package/docs/row-selection.md +136 -0
  34. package/docs/row-styling.md +133 -0
  35. package/docs/scrolling.md +112 -0
  36. package/docs/server-query.md +246 -0
  37. package/docs/server-side.md +206 -0
  38. package/docs/sorting.md +101 -0
  39. package/docs/styling.md +126 -0
  40. package/docs/summary-row.md +76 -0
  41. package/docs/testing.md +744 -0
  42. package/docs/toolbar.md +161 -0
  43. package/docs/use-tm-data-grid.md +361 -0
  44. package/package.json +22 -46
  45. package/skills/appearance/SKILL.md +72 -19
  46. package/skills/cell-selection/SKILL.md +49 -48
  47. package/skills/columns/SKILL.md +125 -70
  48. package/skills/columns/references/columns-api.md +59 -0
  49. package/skills/data/SKILL.md +112 -18
  50. package/skills/editing/SKILL.md +76 -42
  51. package/skills/editing/references/common-mistakes.md +77 -69
  52. package/skills/editing/references/editing-api.md +25 -20
  53. package/skills/editing/references/editors-and-validation.md +80 -18
  54. package/skills/filtering/SKILL.md +155 -41
  55. package/skills/getting-started/SKILL.md +116 -16
  56. package/skills/grouping/SKILL.md +31 -16
  57. package/skills/migrating-to-2/SKILL.md +244 -0
  58. package/skills/options/SKILL.md +24 -12
  59. package/skills/rows/SKILL.md +22 -18
  60. package/skills/rows/references/rows-api.md +10 -6
  61. package/skills/server-side/SKILL.md +170 -17
  62. package/skills/testing/SKILL.md +150 -32
  63. package/skills/testing-components/SKILL.md +230 -0
  64. package/skills/testing-editing/SKILL.md +240 -0
  65. package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
  66. package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
  67. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
  68. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
  69. package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +5 -51
  70. package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
  71. package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
  72. package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
  73. package/src/components/TMDataGridExportPicker.module.css +77 -0
  74. package/src/components/TMDataGridExportPicker.tsx +234 -0
  75. package/src/components/TMDataGridFilterPanel.module.css +54 -0
  76. package/src/components/TMDataGridFilterPanel.tsx +348 -0
  77. package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
  78. package/src/components/TMDataGridFilterSurface.module.css +54 -0
  79. package/src/components/TMDataGridFilterSurface.tsx +167 -0
  80. package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
  81. package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
  82. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
  83. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
  84. package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
  85. package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
  86. package/src/components/TMDataGridMenu.tsx +357 -0
  87. package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
  88. package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
  89. package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
  90. package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
  91. package/src/components/TMDataGridToolbar.tsx +181 -0
  92. package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
  93. package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
  94. package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
  95. package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
  96. package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
  97. package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
  98. package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
  99. package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
  100. package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
  101. package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
  102. package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
  103. package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
  104. package/src/components/filters/controlLayout.ts +32 -0
  105. package/src/components/filters/filterControlFor.ts +65 -0
  106. package/src/components/generatedColumns.tsx +187 -0
  107. package/src/{tmdatagrid/components → components}/icons.ts +1 -0
  108. package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
  109. package/src/components/useHideableColumns.ts +52 -0
  110. package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
  111. package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
  112. package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
  113. package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
  114. package/src/core/controlledStateSync.ts +108 -0
  115. package/src/core/deletedRows.ts +34 -0
  116. package/src/core/dom.ts +74 -0
  117. package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
  118. package/src/core/export.ts +704 -0
  119. package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
  120. package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
  121. package/src/core/filterSurface.ts +99 -0
  122. package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
  123. package/src/{tmdatagrid/core → core}/labels.ts +51 -6
  124. package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
  125. package/src/core/pageReset.ts +120 -0
  126. package/src/core/pagination.ts +81 -0
  127. package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
  128. package/src/{tmdatagrid/core → core}/summary.ts +20 -4
  129. package/src/{tmdatagrid/index.ts → index.ts} +69 -35
  130. package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
  131. package/src/useTMDataGridExport.ts +78 -0
  132. package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
  133. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
  134. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
  135. package/src/tmdatagrid/core/cellExport.ts +0 -320
  136. /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
  137. /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
  138. /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
  139. /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
  140. /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
  141. /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
  142. /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
  143. /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
  144. /package/src/{tmdatagrid/core → core}/capabilities.ts +0 -0
  145. /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
  146. /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
  147. /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
  148. /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
  149. /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
  150. /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
  151. /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
  152. /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
  153. /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
  154. /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
  155. /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
  156. /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
  157. /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
@@ -0,0 +1,149 @@
1
+ # Clicks and context menus
2
+
3
+ Handling clicks in the body: a row click, a cell click, a double-click, and a
4
+ right-click for a menu.
5
+
6
+ Every handler here **composes** with what the click already does. Setting
7
+ `onRowClick` does not replace selection or the highlight; both still happen,
8
+ and your handler runs as well.
9
+
10
+ ```tsx
11
+ <TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
12
+ ```
13
+
14
+ Pass the row type, as above, and `row.original` is typed.
15
+
16
+ ```demo
17
+ file: rows/ClickAndContextMenu.tsx
18
+ hint: Click, double-click and right-click anywhere in the body.
19
+ ```
20
+
21
+ ## The click handlers
22
+
23
+ | Prop | Argument | Notes |
24
+ | --- | --- | --- |
25
+ | `onRowClick` | `row` | Rows show a pointer cursor when set. |
26
+ | `onCellClick` | `{ cell, row, column, event }` | The cell cursor still moves. |
27
+ | `onCellDoubleClick` | same | A double-click that opens an editor still does. |
28
+ | `onCellContextMenu` | same | `renderRowContextMenu` and the cell-selection menu still open. |
29
+
30
+ None of the four fires on a group row: a group row is built on its first
31
+ child's record rather than on one of its own.
32
+
33
+ ## Context menus
34
+
35
+ Right-clicking a row opens a Mantine `Menu` at the pointer. The grid renders the
36
+ `Menu` and its `Menu.Dropdown`, opens it at the cursor, and closes it on Escape,
37
+ on an outside click, on a body scroll, and after an item is picked. The render
38
+ prop supplies only the contents, so anything valid in a dropdown works:
39
+ `Menu.Item`, `Menu.Label`, `Menu.Divider`, `Menu.Sub`, or your own components.
40
+
41
+ ```tsx
42
+ <TMDataGrid.Table<Employee>
43
+ renderRowContextMenu={({ row, cell }) => (
44
+ <>
45
+ <Menu.Label>{row.original.firstName}</Menu.Label>
46
+ <Menu.Item onClick={() => open(row.original.id)}>Open</Menu.Item>
47
+ <Menu.Item
48
+ onClick={() =>
49
+ navigator.clipboard.writeText(String(cell?.getValue() ?? ""))
50
+ }
51
+ >
52
+ Copy cell value
53
+ </Menu.Item>
54
+ <Menu.Divider />
55
+ <Menu.Item color="red" onClick={() => remove(row.original.id)}>
56
+ Delete
57
+ </Menu.Item>
58
+ </>
59
+ )}
60
+ />
61
+ ```
62
+
63
+ | Argument | Type | Description |
64
+ | --- | --- | --- |
65
+ | `table` | `Table<TMDataGridFeatures, TData>` | For actions that read wider state, such as `getSelectedRowModel()`. |
66
+ | `row` | `Row<TMDataGridFeatures, TData>` | The right-clicked row. |
67
+ | `cell` | `Cell<…> \| null` | The cell under the pointer, so a per-cell action such as "copy value" is possible. `null` only if a custom cell renderer stopped the event. |
68
+ | `close` | `() => void` | Closes the menu. `Menu.Item` already closes on click, so this is for content that is not a menu item. |
69
+
70
+ The render prop is called **during render**, and only for the row whose menu is
71
+ open. Keep it a pure function of its arguments and do the work in the item
72
+ handlers.
73
+
74
+ Return `null` to leave a row without a menu. The browser's own context menu
75
+ stays suppressed over the grid either way:
76
+
77
+ ```tsx
78
+ renderRowContextMenu={({ row }) => (row.original.locked ? null : <Menu.Item>Edit</Menu.Item>)}
79
+ ```
80
+
81
+ ### The right-clicked row
82
+
83
+ A right-click does not select or highlight the row. The grid marks it with
84
+ `data-context-menu` while its menu is open, which gives it the hover background.
85
+ An action that should apply to a multi-selection can read the selection off
86
+ `table` and fall back to the clicked row:
87
+
88
+ ```tsx
89
+ renderRowContextMenu={({ table, row }) => {
90
+ const selected = table.getSelectedRowModel().rows;
91
+ const targets = selected.some((r) => r.id === row.id) ? selected : [row];
92
+ return <Menu.Item onClick={() => archive(targets)}>Archive {targets.length}</Menu.Item>;
93
+ }}
94
+ ```
95
+
96
+ ### Taking the whole menu
97
+
98
+ Under `cellSelection: "range"` a right-click inside the selection opens the
99
+ copy and export items too. By default they appear above a divider and yours
100
+ below, which is what happens when the render prop does not use `internalItems`.
101
+ See [Cell selection](/docs/cell-selection#copy-and-export).
102
+
103
+ `internalItems` is those built-in items. **Using it takes over the
104
+ composition**: the menu becomes exactly what you return, in the order you
105
+ return it.
106
+
107
+ ```tsx
108
+ renderRowContextMenu={({ row, internalItems }) => (
109
+ <>
110
+ <Menu.Item onClick={() => open(row.id)}>Open</Menu.Item>
111
+ <Menu.Divider />
112
+ {internalItems}
113
+ </>
114
+ )}
115
+ ```
116
+
117
+ Accept `internalItems` without rendering it to drop the built-in items
118
+ entirely.
119
+
120
+ ### Menu options
121
+
122
+ `rowContextMenuProps` is passed to the Mantine `Menu` unchanged, apart from its
123
+ open state:
124
+
125
+ ```tsx
126
+ <TMDataGrid.Table<Employee>
127
+ renderRowContextMenu={items}
128
+ rowContextMenuProps={{ width: 260, shadow: "lg", position: "right-start" }}
129
+ />
130
+ ```
131
+
132
+ On touch devices a long press (500 ms) opens the same menu. Mantine sets
133
+ `user-select: none` on the element it attaches a context menu to, so body cell
134
+ text is not selectable with the mouse in a grid that has one.
135
+
136
+ ## Reference
137
+
138
+ | Name | Kind | Type | Default | What it does |
139
+ | --- | --- | --- | --- | --- |
140
+ | `onRowClick` | Table prop | `(row) => void` | – | Row click. Adds a pointer cursor. |
141
+ | `onCellClick` | Table prop | `(args) => void` | – | Cell click. Receives `{ cell, row, column, event }`. |
142
+ | `onCellDoubleClick` | Table prop | `(args) => void` | – | Cell double-click. |
143
+ | `onCellContextMenu` | Table prop | `(args) => void` | – | Cell right-click. |
144
+ | `renderRowContextMenu` | Table prop | `({ table, row, cell, close, internalItems }) => ReactNode` | – | Contents of the row's context menu. `null` for no menu. |
145
+ | `TMDataGridRowContextMenuArgs` | Type | `{ table, row, cell, close, internalItems }` | – | What `renderRowContextMenu` receives. |
146
+ | `renderColumnMenuItems` | Table prop | `TMDataGridColumnMenuItemsRenderer` | – | Sets the column menu's contents. An empty list removes the menu button. See [Column header menu](/docs/column-menu). |
147
+ | `rowContextMenuProps` | Table prop | `MenuProps` | – | Passed to the Mantine `Menu`. |
148
+ | `TMDataGridCellEventArgs` | Export | type | – | The argument the three cell handlers receive. |
149
+ | `data-context-menu` | Data attribute | – | – | On the row whose menu is open. |
@@ -0,0 +1,132 @@
1
+ # Row pinning and numbering
2
+
3
+ Two independent features: rows stuck to the top or bottom of the body so they
4
+ stay in view, and a gutter numbering the rows.
5
+
6
+ ## Pinning rows
7
+
8
+ Opt in with `enableRowPinning: true`, or a per-row predicate.
9
+
10
+ ```tsx
11
+ const grid = useTMDataGrid({ data, columns, enableRowPinning: true });
12
+ ```
13
+
14
+ Pinned rows leave the scrolling order and render in sticky blocks: top-pinned
15
+ rows under the header (and under the entry block while one is open),
16
+ bottom-pinned rows above the summary row.
17
+
18
+ There is no built-in pin gesture and no pin icon. The grid provides `row.pin()`
19
+ on every row, callable from anywhere a row is rendered. Two places are
20
+ convenient for it.
21
+
22
+ A **lane of your own**, a display column whose cell is a pin button:
23
+
24
+ ```tsx
25
+ function PinToggle({ row }: { row: Row<TMDataGridFeatures, Employee> }) {
26
+ // Subscribed rather than called in the component body: the React Compiler
27
+ // caches a bare call along with the stable `row` identity.
28
+ const pinned = useSelector(row.table.store, () => row.getIsPinned());
29
+ return (
30
+ <ActionIcon
31
+ variant={pinned === false ? "subtle" : "light"}
32
+ color={pinned === false ? "gray" : "blue"}
33
+ aria-label={pinned === false ? "Pin to top" : "Unpin"}
34
+ // The row underneath may select or highlight on click.
35
+ onClick={(event) => {
36
+ event.stopPropagation();
37
+ row.pin(pinned === false ? "top" : false);
38
+ }}
39
+ >
40
+ {pinned === false ? <IconPin size={16} /> : <IconPinFilled size={16} />}
41
+ </ActionIcon>
42
+ );
43
+ }
44
+
45
+ const pinColumn = columnHelper.display({
46
+ id: "pin",
47
+ header: "",
48
+ // Columns are fluid - `minmax(minSize, flex fr)` - so a control lane states
49
+ // one width three times to opt out. See [Sizing](/docs/column-layout#sizing).
50
+ size: 44,
51
+ minSize: 44,
52
+ maxSize: 44,
53
+ meta: { label: "Pin", align: "center" },
54
+ enableResizing: false,
55
+ enableSorting: false,
56
+ cell: ({ row }) => <PinToggle row={row} />,
57
+ });
58
+ ```
59
+
60
+ Or the **row context menu**, which reaches a row at either edge as well as one
61
+ in the body:
62
+
63
+ ```tsx
64
+ <TMDataGrid.Table<Employee>
65
+ renderRowContextMenu={({ row }) => (
66
+ <>
67
+ {row.getIsPinned() !== "top" && (
68
+ <Menu.Item onClick={() => row.pin("top")}>Pin to top</Menu.Item>
69
+ )}
70
+ {row.getIsPinned() !== false && (
71
+ <Menu.Item onClick={() => row.pin(false)}>Unpin</Menu.Item>
72
+ )}
73
+ </>
74
+ )}
75
+ />
76
+ ```
77
+
78
+ Include a way to unpin.
79
+
80
+ ```demo
81
+ file: rows/PinningAndNumbers.tsx
82
+ hint: Click a pin, or right-click a row for either edge, then scroll.
83
+ ```
84
+
85
+ `row.pin("top" | "bottom" | false)`, `row.getIsPinned()` and `row.getCanPin()`
86
+ are TanStack's own APIs; the state is `rowPinning: { top: string[], bottom:
87
+ string[] }`, settable wholesale with `table.setRowPinning()` or seeded through
88
+ `initialState`.
89
+
90
+ ### Pinned row behaviour
91
+
92
+ Selection, editing, details, the context menu and per-row styling all behave as
93
+ they do in the body. Pinned rows are excluded only from the features that
94
+ depend on scroll *order* - striping and the cell range - and the row-number
95
+ gutter leaves them unnumbered.
96
+
97
+ Also note:
98
+
99
+ - A pinned row stays at its edge even when a filter or the pager would have
100
+ dropped it from the body.
101
+ - A pinned id whose row leaves `data` (a delete, a server-side page swap) is not
102
+ shown. It stays in state and the row returns to its edge if its data comes
103
+ back.
104
+ - **Group rows never pin.** A leaf whose group is collapsed stays hidden while
105
+ pinned.
106
+ - `rowPinning` is **not** persisted. Row ids are data, and the settings group
107
+ holds layout only.
108
+
109
+ ## Numbering rows
110
+
111
+ `enableRowNumbers: true` adds a gutter, outermost left, before every other
112
+ lane.
113
+
114
+ ```tsx
115
+ const grid = useTMDataGrid({ data, columns, enableRowNumbers: true });
116
+ ```
117
+
118
+ It numbers **the current view**: sorted, filtered, and continuing across pages
119
+ rather than restarting on each one. The number is a position, not an identifier
120
+ for the record; for that, add a column of your own over the record's id. Group
121
+ rows and pinned rows take no number.
122
+
123
+ ## Reference
124
+
125
+ | Name | Kind | Type | Default | What it does |
126
+ | --- | --- | --- | --- | --- |
127
+ | `enableRowPinning` | Table option | `boolean \| (row) => boolean` | `false` | Whether rows can be pinned. |
128
+ | `initialState.rowPinning` | Table option | `{ top: string[], bottom: string[] }` | empty | Rows pinned at mount. Not persisted. |
129
+ | `enableRowNumbers` | Option | `boolean` | `false` | Adds the row-number gutter. |
130
+ | `row.pin` | Row method | `("top" \| "bottom" \| false) => void` | – | Pins or unpins one row. |
131
+ | `row.getIsPinned` | Row method | `() => "top" \| "bottom" \| false` | – | Where a row is pinned. |
132
+ | `ROW_NUMBER_COLUMN_ID` | Export | `"__rowNumber__"` | – | Id of the generated number gutter. |
@@ -0,0 +1,136 @@
1
+ # Row selection
2
+
3
+ Picking rows out of the grid, and reading back what was picked. The grid
4
+ separates two things: a set of rows for a bulk action, and a single highlighted
5
+ row. `selectionMode` sets which of them a click drives, and defaults to
6
+ `"checkbox"`.
7
+
8
+ ```tsx
9
+ const grid = useTMDataGrid({ data, columns }); // "checkbox"
10
+ const rows = useTMDataGrid({ data, columns, selectionMode: "row" });
11
+ const master = useTMDataGrid({ data, columns, selectionMode: "highlight" });
12
+ ```
13
+
14
+ ```demo
15
+ file: rows/SelectionModes.tsx
16
+ ```
17
+
18
+ ## The four modes
19
+
20
+ `selectionMode` sets what selecting looks like and what a bare row click does.
21
+ A click can toggle a multi-selection or move a highlight, not both, so the two
22
+ are one option rather than two.
23
+
24
+ | Mode | Checkbox column | Row click |
25
+ | ---- | --------------- | --------- |
26
+ | `"checkbox"` | yes, multi-select | nothing |
27
+ | `"row"` | no | selects, with the usual modifiers |
28
+ | `"checkboxAndHighlight"` | yes, multi-select | highlights one row |
29
+ | `"highlight"` | no | highlights one row - no selection at all |
30
+
31
+ The first two write to TanStack's `rowSelection`, so the toolbar count,
32
+ `getSelectedRowModel()` and persistence all behave the same either way.
33
+
34
+ Under `"row"` the click follows the usual desktop-list conventions: a plain
35
+ click replaces the selection with this row, Ctrl/Cmd toggles it and leaves the
36
+ rest, Shift selects the range from the anchor, Ctrl+Shift adds that range. Rows are
37
+ focusable in this mode, and Space or Enter toggles the focused row.
38
+
39
+ `enableRowSelection: false` removes the checkbox column and row-click
40
+ selection in any mode. It has no effect under `"highlight"`, which selects
41
+ nothing.
42
+
43
+ ## The highlighted row
44
+
45
+ The highlighted row is state of its own rather than a slice of `rowSelection`,
46
+ so `"checkboxAndHighlight"` runs both at once: tick rows for a bulk action,
47
+ click one to open its detail panel beside the grid.
48
+
49
+ `defaultHighlightedRowId` seeds it and `onHighlightedRowChange` follows it,
50
+ typically into a route for a master–detail view:
51
+
52
+ ```tsx
53
+ const grid = useTMDataGrid({
54
+ data,
55
+ columns,
56
+ selectionMode: "highlight",
57
+ onHighlightedRowChange: (rowId) =>
58
+ navigate({ to: "/employees/$id", params: { id: rowId ?? "" } }),
59
+ });
60
+ ```
61
+
62
+ For a panel that opens under the row instead of beside the grid, see
63
+ [Row details](/docs/row-details). A grid can use both.
64
+
65
+ ## Acting on a selection
66
+
67
+ Read the selection through the table store with TanStack Store's
68
+ [`useSelector`](https://tanstack.com/store/latest/docs/framework/react/reference), rather
69
+ than by calling a method directly. The table identity is stable across renders,
70
+ so the React Compiler caches a bare `getSelectedRowModel()` call and the
71
+ toolbar stops updating.
72
+
73
+ ```tsx
74
+ const selected = useSelector(
75
+ grid.table.store,
76
+ () => grid.table.getSelectedRowModel().rows,
77
+ );
78
+ ```
79
+
80
+ ```demo
81
+ file: rows/SelectionState.tsx
82
+ hint: Tick a few rows - the toolbar turns into a bulk-action bar and back.
83
+ ```
84
+
85
+ ## Highlighting selected rows
86
+
87
+ `showSelectedBackground` follows the mode: on for `"row"`, off for
88
+ `"checkbox"`. Set it explicitly for checkboxes and a background, or for row
89
+ selection with no background change.
90
+
91
+ ```tsx
92
+ const grid = useTMDataGrid({ data, columns, showSelectedBackground: true });
93
+ ```
94
+
95
+ The colour is `--dg-row-selected-bg`, which defaults to
96
+ `--mantine-primary-color-light`. Change it on the grid element rather than
97
+ touching the flag:
98
+
99
+ ```tsx
100
+ <TMDataGrid
101
+ {...grid}
102
+ style={{ "--dg-row-selected-bg": "color-mix(in srgb, var(--mantine-color-blue-6) 12%, transparent)" }}
103
+ />
104
+ ```
105
+
106
+ Rows carry `data-selected` whenever they are selected, `data-selected-bg` when
107
+ they also take the background, and `data-highlighted` on the highlighted row.
108
+ [Custom styling](/docs/row-styling) can key off any of them.
109
+
110
+ ## Group rows
111
+
112
+ A group row's checkbox selects every record under it, at any depth, including
113
+ records inside collapsed sub-groups. It shows a tick once all of them are
114
+ selected and a dash while only some are. Only the records are written to
115
+ `rowSelection`. See [Grouping](/docs/grouping#selection).
116
+
117
+ ## Reference
118
+
119
+ | Name | Kind | Type | Default | What it does |
120
+ | --- | --- | --- | --- | --- |
121
+ | `selectionMode` | Option | `"checkbox" \| "row" \| "checkboxAndHighlight" \| "highlight"` | `"checkbox"` | What selecting looks like and what a row click does. |
122
+ | `TMDataGridSelectionMode` | Type | – | – | The type of `selectionMode`. |
123
+ | `enableRowSelection` | Table option | `boolean \| (row) => boolean` | `true` | `false` removes the checkbox column and row-click selection. |
124
+ | `enableMultiRowSelection` | Table option | `boolean` | `true` | `false` limits the selection to one row and drops group checkboxes. |
125
+ | `showSelectedBackground` | Option | `boolean` | Follows the mode | Whether selected rows take a background tint. |
126
+ | `defaultHighlightedRowId` | Option | `string \| null` | `null` | Row highlighted at mount. |
127
+ | `onHighlightedRowChange` | Callback | `(rowId: string \| null) => void` | – | Fires when the highlight moves. |
128
+ | `SELECT_COLUMN_ID` | Export | `"__select__"` | – | Id of the generated checkbox column. |
129
+ | `getSelectableRowIds` | Export | `(table) => string[]` | – | Ids the header checkbox would select. |
130
+ | `resolveRowSelectionClick` | Export | `(args) => ResolvedRowSelection` | – | The desktop-list click rules, for a custom surface. |
131
+ | `ResolveRowSelectionClickArgs` · `TMDataGridRowClickModifiers` | Types | – | – | What `resolveRowSelectionClick` takes, and its `modifiers`: `{ toggle, extend }`. |
132
+ | `--dg-row-selected-bg` | CSS variable | colour | `--mantine-primary-color-light` | Selected row background. |
133
+ | `--dg-row-highlight-bg` | CSS variable | colour | Themed | Highlighted row background. |
134
+ | `data-selected` | Data attribute | – | – | On every selected row. |
135
+ | `data-selected-bg` | Data attribute | – | – | On selected rows that also take the background. |
136
+ | `data-highlighted` | Data attribute | – | – | On the highlighted row. |
@@ -0,0 +1,133 @@
1
+ # Row styling
2
+
3
+ Colouring rows by their contents, such as an overdue invoice in red.
4
+
5
+ ```tsx
6
+ <TMDataGrid.Table<Employee>
7
+ rowStyle={(row) =>
8
+ !row.getIsGrouped() && row.original.status === "Terminated"
9
+ ? { "--row-bg": "color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent)" }
10
+ : undefined
11
+ }
12
+ />
13
+ ```
14
+
15
+ Group rows are handed to `rowStyle` and `rowClassName` too, and a group row's `original` is an arbitrary child's record.
16
+ Guard a callback that reads `original` with `row.getIsGrouped()`, or match `data-grouped="true"` to style the group rows themselves.
17
+
18
+ ```demo
19
+ file: rows/RowStyling.tsx
20
+ hint: Select and hover a coloured row - both still show through the row background.
21
+ ```
22
+
23
+ ## Set the row background
24
+
25
+ Set `--row-bg` rather than `background`. Hover, selection, the highlight, the
26
+ cell range and striping each add a background over the row and are composed
27
+ against `--row-bg`; `background` overrides all of them, so a coloured row stops
28
+ responding to hover and selection.
29
+
30
+ ```tsx
31
+ rowStyle={() => ({ background: "pink" })} // hover and selection hidden
32
+ rowStyle={() => ({ "--row-bg": "pink" })} // both still show
33
+ ```
34
+
35
+ `rowStyle` accepts `CSSProperties` or an object of custom properties. The type
36
+ is a union, so a callback returning either compiles.
37
+
38
+ Pick a colour that reads under both colour schemes. A `-0` Mantine shade
39
+ (`red-0`) is near-white, which turns unreadable behind light text in the dark
40
+ scheme; mixing a mid shade into transparency tints both schemes evenly:
41
+ `color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent)`.
42
+
43
+ The grid composes `--row-bg` over the theme's body colour, so a translucent value tints the row and stays opaque under the pinned columns and the pinned rows.
44
+
45
+ ## One cell, not the row
46
+
47
+ There is no `cellStyle` or `cellClassName`. Style a single cell from the
48
+ column's own `cell` renderer, which owns the element you want to colour:
49
+
50
+ ```tsx
51
+ columnHelper.accessor("age", {
52
+ cell: ({ getValue }) => {
53
+ const age = getValue();
54
+ return <span style={{ color: age > 60 ? "var(--mantine-color-red-6)" : undefined }}>{age}</span>;
55
+ },
56
+ });
57
+ ```
58
+
59
+ From a stylesheet, match the cell's `data-column-id` under a `rowClassName`.
60
+ Body cells carry no `data-dg-part`; the coordinate attributes identify them:
61
+
62
+ ```css
63
+ .overdue [data-column-id="dueDate"] {
64
+ font-weight: 600;
65
+ }
66
+ ```
67
+
68
+ ## Classes instead
69
+
70
+ `rowClassName` takes the same shape and adds to the grid's own classes, for
71
+ when the styling belongs in a stylesheet:
72
+
73
+ ```tsx
74
+ <TMDataGrid.Table<Invoice>
75
+ rowClassName={(row) => (row.original.overdue ? classes.overdue : undefined)}
76
+ />
77
+ ```
78
+
79
+ ```css
80
+ .overdue {
81
+ --row-bg: color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent);
82
+ font-weight: 600;
83
+ }
84
+ ```
85
+
86
+ ## Striping
87
+
88
+ `striped` gives every second row `--dg-row-striped-bg`. Striping follows the
89
+ row's **position in the view**, so it survives sorting, filtering and
90
+ virtualization rather than sticking to particular records.
91
+
92
+ ```tsx
93
+ <TMDataGrid.Table striped />
94
+ ```
95
+
96
+ Pinned rows are not striped: they have left the scrolling order.
97
+
98
+ ## Styling by state
99
+
100
+ Rows carry data attributes for their state, so a stylesheet can target any of
101
+ it without a callback:
102
+
103
+ | Attribute | On |
104
+ | --- | --- |
105
+ | `data-selected` | Selected rows |
106
+ | `data-selected-bg` | Selected rows that also take the background |
107
+ | `data-highlighted` | The highlighted row |
108
+ | `data-grouped` | Group rows |
109
+ | `data-depth` | Every row. The nesting level |
110
+ | `data-context-menu` | The row whose context menu is open |
111
+ | `data-row-id` | Every row. Its id, which [tests](/docs/testing) key off |
112
+
113
+ The state attributes are present, with the value `"true"`, only on the rows
114
+ they apply to, so the bare attribute and the value match the same rows:
115
+
116
+ ```css
117
+ [data-dg-part="row"][data-grouped="true"] {
118
+ --row-bg: color-mix(in srgb, var(--mantine-color-gray-6) 12%, transparent);
119
+ font-weight: 600;
120
+ }
121
+ ```
122
+
123
+ ## Reference
124
+
125
+ | Name | Kind | Type | Default | What it does |
126
+ | --- | --- | --- | --- | --- |
127
+ | `rowStyle` | Table prop | `TMDataGridRowStyle \| (row) => TMDataGridRowStyle` | – | Inline style for a body row. |
128
+ | `rowClassName` | Table prop | `string \| (row) => string \| undefined` | – | Class for a body row, added after the grid's own. |
129
+ | `striped` | Table prop | `boolean` | `false` | Every second row takes `--dg-row-striped-bg`. |
130
+ | `TMDataGridRowStyle` | Export | type | – | `CSSProperties` or an object of `--*` custom properties. |
131
+ | `--row-bg` | CSS variable | colour | – | The row's own background, composed over the body colour and under hover, selection and range. |
132
+ | `--dg-row-striped-bg` | CSS variable | colour | Themed | The stripe colour. |
133
+ | `--dg-row-height` | CSS variable | length | From `size` | Row height. `meta.rowHeight` is the supported way to change it. |
@@ -0,0 +1,112 @@
1
+ # Scrolling and virtualization
2
+
3
+ Virtualization is **always on**. There is no flag and no threshold: only the
4
+ rows within the viewport, plus a small overscan, are mounted, at any row
5
+ count.
6
+
7
+ ```tsx
8
+ const grid = useTMDataGrid({ data, columns }); // 200 rows or 200 000
9
+ ```
10
+
11
+ Rows only. Columns are not virtualized: every column that is visible is in the
12
+ DOM, header and body alike, however far off-screen it sits. A grid with a few
13
+ dozen columns is fine; hide the ones a user does not need rather than relying
14
+ on the viewport to do it.
15
+
16
+ ## Overscan
17
+
18
+ How many rows stay mounted on each side of the viewport. Defaults to `6`.
19
+
20
+ ```tsx
21
+ const grid = useTMDataGrid({ data, columns, overscan: 12 });
22
+ ```
23
+
24
+ Raise it if a fast scroll flashes blank rows; lower it when rows are expensive
25
+ to render.
26
+
27
+ ## Row height
28
+
29
+ Taken from `meta.rowHeight`, or from the `size` prop when that is not set. Rows
30
+ are **fixed height**, so the virtualizer's estimate is exact and the scrollbar
31
+ does not drift as you scroll.
32
+
33
+ ```tsx
34
+ const grid = useTMDataGrid({ data, columns, meta: { rowHeight: 64 } });
35
+ ```
36
+
37
+ [Row details](/docs/row-details) are the exception: a row showing a panel is as
38
+ tall as the panel, so those rows are measured after they mount.
39
+ `renderDetailsEstHeight` is what the virtualizer assumes for one it has not
40
+ measured yet.
41
+
42
+ ## Scrolling to a row
43
+
44
+ ```tsx
45
+ const { scrollToRow } = useTMDataGrid({ data, columns, getRowId });
46
+
47
+ scrollToRow({ rowId: "42", align: "center" });
48
+ ```
49
+
50
+ Under virtualization the target row may not be mounted, so
51
+ `element.scrollIntoView()` cannot find it. `align` is `"start"`, `"center"`,
52
+ `"end"` or `"auto"`, which scrolls only if the row is out of view.
53
+
54
+ It answers whether the row could be reached. `false` means the row is not in
55
+ the current view - filtered out, on another page, or an id matching no row -
56
+ and nothing scrolled. A pinned row answers `true` without scrolling.
57
+
58
+ The hook reaches the virtualizer through `scrollerRef`, which `TMDataGrid.Table`
59
+ fills in. That is internal wiring rather than an API: spread the whole grid
60
+ object onto `<TMDataGrid>`, because a hand-assembled prop list that leaves it
61
+ out has no scrolling.
62
+
63
+ While the draft store is running, `TMDataGrid.DraftActions` hands its
64
+ `renderActions` slot an `actions.scrollToFirstOpenRow(align?)` that goes to the
65
+ first row [left open](/docs/draft-store), without your having to
66
+ track the ids.
67
+
68
+ ## Edge callbacks
69
+
70
+ `TMDataGrid.Table` reports arrivals at each edge, firing **once** when the
71
+ scroll reaches it rather than on every scroll event:
72
+
73
+ ```tsx
74
+ <TMDataGrid.Table
75
+ onScrollToBottom={() => console.log("at the end")}
76
+ onScrollToRight={() => console.log("at the last column")}
77
+ />
78
+ ```
79
+
80
+ For loading more rows, use [`onReachEnd`](/docs/server-side#infinite-scroll)
81
+ instead. It fires a number of rows before the bottom, and latches per row count
82
+ so a pending fetch is not requested twice.
83
+
84
+ ## Scroll shadows
85
+
86
+ Two soft shadows, both driven by `animation-timeline: scroll(…)` rather than by
87
+ a scroll listener.
88
+
89
+ **Under the header.** Once body rows scroll beneath the sticky header, a shadow
90
+ appears along its bottom edge, indicating rows above the viewport. A grid with
91
+ nothing to scroll shows none. `--dg-header-shadow-color` recolours it.
92
+
93
+ **Beside a pinned lane.** A [pinned column](/docs/column-layout#pinning) shows a
94
+ band over the data next to it, and only while it is covering content: it fades
95
+ in over the first 20px of horizontal scroll and fades out again at the far
96
+ end.
97
+
98
+ Where `animation-timeline` is unsupported, the header's own border draws the
99
+ boundary and the pinned band is always on.
100
+
101
+ ## Reference
102
+
103
+ | Name | Kind | Type | Default | What it does |
104
+ | --- | --- | --- | --- | --- |
105
+ | `overscan` | Option | `number` | `6` | Rows kept mounted beyond each edge of the viewport. |
106
+ | `meta.rowHeight` | Option | `number` | From `size` | Row height, in pixels. The virtualizer needs a number. |
107
+ | `scrollToRow` | Hook return | `({ rowId, align? }) => boolean` | `align: "auto"` | Scrolls a row into view, mounted or not. Answers whether it could be reached. |
108
+ | `TMDataGridScrollToRowArgs` | Type | `{ rowId, align? }` | – | What `scrollToRow` takes. |
109
+ | `onScrollToTop` · `onScrollToBottom` · `onScrollToLeft` · `onScrollToRight` | Table props | `() => void` | – | Fire once on arriving at that edge. |
110
+ | `TMDataGridScrollAlign` | Export | `"start" \| "center" \| "end" \| "auto"` | – | The `align` argument. |
111
+ | `--dg-header-shadow-color` | CSS variable | colour | Themed | The shadow under the sticky header. |
112
+ | `--dg-sticky-edge-range` | CSS variable | length | `20px` | How far the pinned-lane band takes to fade in. |