@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.
- package/README.md +5 -212
- package/dist/index.d.ts +1664 -632
- package/dist/index.js +5226 -3223
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/anatomy.md +102 -0
- package/docs/cell-selection.md +154 -0
- package/docs/column-layout.md +204 -0
- package/docs/columns.md +262 -0
- package/docs/components.md +304 -0
- package/docs/editing.md +603 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +326 -0
- package/docs/filtering.md +358 -0
- package/docs/getting-started.md +123 -0
- package/docs/grouping.md +165 -0
- package/docs/loading-and-empty.md +92 -0
- package/docs/localization.md +79 -0
- package/docs/menu.md +143 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +111 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +175 -0
- package/docs/quick-search.md +83 -0
- package/docs/row-details.md +113 -0
- package/docs/row-interaction.md +148 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +134 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +111 -0
- package/docs/server-query.md +246 -0
- package/docs/server-side.md +206 -0
- package/docs/sorting.md +101 -0
- package/docs/styling.md +126 -0
- package/docs/summary-row.md +76 -0
- package/docs/testing.md +309 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +21 -45
- package/skills/appearance/SKILL.md +70 -17
- package/skills/cell-selection/SKILL.md +70 -76
- package/skills/columns/SKILL.md +131 -32
- package/skills/data/SKILL.md +100 -23
- package/skills/editing/SKILL.md +217 -96
- package/skills/editing/references/common-mistakes.md +111 -24
- package/skills/editing/references/editing-api.md +63 -39
- package/skills/editing/references/editors-and-validation.md +77 -19
- package/skills/filtering/SKILL.md +148 -40
- package/skills/getting-started/SKILL.md +18 -16
- package/skills/grouping/SKILL.md +32 -15
- package/skills/options/SKILL.md +39 -9
- package/skills/rows/SKILL.md +22 -18
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +10 -7
- package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +7 -19
- package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +7 -1
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +39 -23
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +106 -38
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +4 -4
- package/src/components/TMDataGridDraftActions.tsx +307 -0
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +58 -50
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +150 -115
- package/src/components/TMDataGridExportPicker.module.css +77 -0
- package/src/components/TMDataGridExportPicker.tsx +234 -0
- package/src/components/TMDataGridFilterPanel.module.css +54 -0
- package/src/components/TMDataGridFilterPanel.tsx +348 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +7 -5
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -13
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +4 -3
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +10 -0
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +100 -28
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +354 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +12 -7
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +90 -67
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +678 -156
- package/src/components/TMDataGridToolbar.module.css +21 -0
- package/src/components/TMDataGridToolbar.tsx +181 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
- package/src/components/editors/TMDataGridNumberEditor.tsx +70 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/editorShared.ts +17 -31
- package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
- package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
- package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
- package/src/components/filters/controlLayout.ts +32 -0
- package/src/components/filters/filterControlFor.ts +65 -0
- package/src/{tmdatagrid/components → components}/icons.ts +1 -0
- package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
- package/src/components/useHideableColumns.ts +52 -0
- package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
- package/src/{tmdatagrid/core → core}/capabilities.ts +14 -6
- package/src/{tmdatagrid/core → core}/columnOptions.ts +46 -0
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledState.ts +179 -0
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/core/editEngine.ts +2476 -0
- package/src/{tmdatagrid/core → core}/editorFocus.ts +8 -4
- package/src/core/export.ts +843 -0
- package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
- package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
- package/src/core/filterSurface.ts +99 -0
- package/src/{tmdatagrid/core → core}/labels.ts +66 -8
- package/src/{tmdatagrid/core → core}/labelsSv.ts +26 -3
- package/src/core/pageReset.ts +120 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/core/resizePreview.ts +141 -0
- package/src/core/summary.ts +59 -0
- package/src/core/useSettledTableState.ts +36 -0
- package/src/{tmdatagrid/index.ts → index.ts} +75 -12
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +734 -135
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +0 -162
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.module.css +0 -12
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -162
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +0 -40
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- package/src/tmdatagrid/core/editEngine.ts +0 -1006
- package/src/tmdatagrid/core/summary.ts +0 -35
- /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
- /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
- /package/src/{tmdatagrid/core → core}/grouping.ts +0 -0
- /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
- /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
- /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# A query builder form
|
|
2
|
+
|
|
3
|
+
A search form holds dates, a title filter and a list of extra conditions. The
|
|
4
|
+
conditions are rows, so the condition builder is a grid, wrapped as a form
|
|
5
|
+
field: rows come in through `value`, and approved changes go back out through
|
|
6
|
+
`onChange`.
|
|
7
|
+
|
|
8
|
+
```demo
|
|
9
|
+
file: recipes/QueryBuilderForm.tsx
|
|
10
|
+
hint: Add a condition and pick Status - the value editor becomes a select. Delete every condition, or repeat one, and Search says why it will not.
|
|
11
|
+
height: 560
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The form holds the array; the grid holds the row being edited. The grid never
|
|
15
|
+
mutates `data`, so the form's state is only ever what the user has approved.
|
|
16
|
+
|
|
17
|
+
## Wrapping the grid as a field
|
|
18
|
+
|
|
19
|
+
The grid is wrapped once, as an ordinary controlled component:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
function ConditionsGrid({ value, onChange }: {
|
|
23
|
+
value: Array<QueryCondition>;
|
|
24
|
+
onChange: (next: Array<QueryCondition>) => void;
|
|
25
|
+
}) {
|
|
26
|
+
const grid = useTMDataGrid({
|
|
27
|
+
data: value,
|
|
28
|
+
columns,
|
|
29
|
+
getRowId: (row) => String(row.id),
|
|
30
|
+
editing: {
|
|
31
|
+
mode: "row",
|
|
32
|
+
rowValidators,
|
|
33
|
+
onCommit: ({ rowId, value: row }) =>
|
|
34
|
+
onChange(value.map((c) => (String(c.id) === rowId ? row : c))),
|
|
35
|
+
onRowAdd: ({ value: row }) =>
|
|
36
|
+
onChange([...value, { ...row, id: Math.min(0, ...value.map((c) => c.id)) - 1 }]),
|
|
37
|
+
onRowDelete: ({ rowId }) =>
|
|
38
|
+
onChange(value.filter((c) => String(c.id) !== rowId)),
|
|
39
|
+
newRowDefaults: () => ({ id: 0, field: "title", operator: "contains", value: "" }),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
return (
|
|
43
|
+
<TMDataGrid {...grid} style={{ flex: 1, minHeight: 0 }}>
|
|
44
|
+
<TMDataGrid.Table<QueryCondition> />
|
|
45
|
+
</TMDataGrid>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
And handed to the form as a field:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<form.Field
|
|
54
|
+
name="conditions"
|
|
55
|
+
validators={{
|
|
56
|
+
onChange: ({ value }) =>
|
|
57
|
+
value.length === 0 ? "Add at least one condition" : undefined,
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{(field) => (
|
|
61
|
+
<ConditionsGrid value={field.state.value} onChange={field.handleChange} />
|
|
62
|
+
)}
|
|
63
|
+
</form.Field>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`field.handleChange` is the `onChange`: it writes the array into the form and
|
|
67
|
+
runs the field's validators. `onChange` carries the whole next array rather than
|
|
68
|
+
a diff.
|
|
69
|
+
|
|
70
|
+
- **Map by row id, never by index.** A sort or a delete moves the index; the
|
|
71
|
+
draft is keyed by the id.
|
|
72
|
+
- **`data` identity stays stable.** `field.state.value` changes identity only
|
|
73
|
+
when a row is written, which is what `data` requires.
|
|
74
|
+
See [useTMDataGrid](/docs/use-tm-data-grid).
|
|
75
|
+
- **New rows count down from `-1`.** `Math.min(0, ...ids) - 1`, so an emptied
|
|
76
|
+
grid starts at `-1` rather than `-Infinity` and existing negative ids keep
|
|
77
|
+
descending. The grid's `tempId` never leaves the grid: the id you assign in
|
|
78
|
+
`editing.onRowAdd` is the one the form sees.
|
|
79
|
+
|
|
80
|
+
## Where validation belongs
|
|
81
|
+
|
|
82
|
+
A rule that can be decided from one row belongs to the grid. A rule that needs
|
|
83
|
+
the other rows, or the collection as a whole, belongs to the form.
|
|
84
|
+
|
|
85
|
+
| Rule | Owner | Written as |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| "The condition needs a value" | Grid | `editing.rowValidators.onSubmit` |
|
|
88
|
+
| "A date, for a date field" | Grid | `meta.edit.editor` per column |
|
|
89
|
+
| "At least one condition" | Form | the `conditions` field's `onChange` validator |
|
|
90
|
+
| "No two conditions repeat a field and operator" | Form | the same place |
|
|
91
|
+
|
|
92
|
+
A row's form is seeded with that row's values only, so it cannot see the array,
|
|
93
|
+
and the grid's `edit.store` publishes field names but not values, so the form
|
|
94
|
+
cannot see a draft.
|
|
95
|
+
|
|
96
|
+
## Editors that follow the row
|
|
97
|
+
|
|
98
|
+
In a `[field, operator, value]` builder the value cell means something
|
|
99
|
+
different on every row. `meta.edit.editor` receives the row's live form, so one
|
|
100
|
+
component can switch on the *draft* field rather than the committed one, and
|
|
101
|
+
picking a new field resets its dependents:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
const FieldEditor: TMDataGridEditorComponent = ({ field, form, size }) => (
|
|
105
|
+
<Select
|
|
106
|
+
size={size}
|
|
107
|
+
data={[...FIELDS]}
|
|
108
|
+
value={String(field.state.value)}
|
|
109
|
+
onChange={(next) => {
|
|
110
|
+
if (next === null) return;
|
|
111
|
+
field.handleChange(next);
|
|
112
|
+
form.setFieldValue("operator", OPERATORS[next as QueryField][0]);
|
|
113
|
+
form.setFieldValue("value", "");
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The operator and value editors read the sibling with TanStack Store's
|
|
120
|
+
[`useSelector`](https://tanstack.com/store/latest/docs/framework/react/reference):
|
|
121
|
+
`useSelector(form.store, (state) => state.values.field)`, so switching Field
|
|
122
|
+
mid-edit switches them immediately. See
|
|
123
|
+
[Editors and validation](/docs/editors) for the editor's full API.
|
|
124
|
+
|
|
125
|
+
## Which mode
|
|
126
|
+
|
|
127
|
+
`editing.mode: "row"`. A condition is approved as a unit: the pencil opens the
|
|
128
|
+
whole row, Save commits it, and `editing.onCommit` hands the form one finished
|
|
129
|
+
condition. The form's rules run at that point.
|
|
130
|
+
|
|
131
|
+
**Not `draft: true`.** A draft store holds every commit inside the grid and
|
|
132
|
+
calls nothing until `edit.saveDrafts()`, so the form's array goes stale the
|
|
133
|
+
moment typing starts: "at least one condition" counts rows the user may have
|
|
134
|
+
marked for deletion, a duplicate sitting in a draft passes unseen, and the
|
|
135
|
+
form's submit saves an array missing every pending edit.
|
|
136
|
+
|
|
137
|
+
To use the store anyway, invert where the save happens: `grid.edit.saveDrafts()`
|
|
138
|
+
becomes the only way rows reach the form, and the form's submit is gated on the
|
|
139
|
+
grid holding no draft -
|
|
140
|
+
`useSelector(grid.edit.store, (s) => s.openRowIds.length === 0)`.
|
|
141
|
+
|
|
142
|
+
## Submitting
|
|
143
|
+
|
|
144
|
+
The form submits only while the grid holds no draft.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
const hasOpenDraft = useSelector(grid.edit.store, (s) => s.openRowIds.length > 0);
|
|
148
|
+
|
|
149
|
+
<Button type="submit" disabled={!canSubmit || hasOpenDraft}>
|
|
150
|
+
{hasOpenDraft ? "Finish the open condition" : "Search"}
|
|
151
|
+
</Button>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The alternative is to flush instead of block. `await grid.edit.commitAll()`
|
|
155
|
+
before `form.handleSubmit()` submits every open row through the normal
|
|
156
|
+
`editing.onCommit` path, in every mode; with `draft: true` follow it with
|
|
157
|
+
`grid.edit.saveDrafts()`. Rows that fail their own validation
|
|
158
|
+
stay open, so the submit fails.
|
|
159
|
+
|
|
160
|
+
Two constraints apply to a native `<form>` around a grid:
|
|
161
|
+
|
|
162
|
+
- The grid's buttons cannot submit it. Mantine buttons default to
|
|
163
|
+
`type="button"`, so only your own `type="submit"` button submits.
|
|
164
|
+
- Enter inside a cell editor commits the cell, not the form. The editor
|
|
165
|
+
prevents the default, so implicit form submission never fires.
|
|
166
|
+
|
|
167
|
+
## Reference
|
|
168
|
+
|
|
169
|
+
The pieces this recipe composes:
|
|
170
|
+
|
|
171
|
+
| Piece | Documented on |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `data`, `getRowId`, `editing.mode`, `editing.onCommit`, `editing.onRowAdd`, `editing.onRowDelete`, `editing.newRowDefaults`, `edit.store` | [Editing](/docs/editing) |
|
|
174
|
+
| `editing.rowValidators`, `meta.edit.editor`, the editor contract | [Editors and validation](/docs/editors) |
|
|
175
|
+
| `useForm`, `form.Field`, field validators | TanStack Form's own docs |
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Quick search
|
|
2
|
+
|
|
3
|
+
One box over every column. `TMDataGrid.Search` is a debounced input writing
|
|
4
|
+
TanStack's `globalFilter` state. There is no option to turn it on: render the
|
|
5
|
+
component, or do not.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<TMDataGrid.Toolbar>
|
|
9
|
+
<TMDataGrid.Search />
|
|
10
|
+
</TMDataGrid.Toolbar>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```demo
|
|
14
|
+
file: data/QuickSearch.tsx
|
|
15
|
+
hint: Try “Stckholm”. Fuzzy finds it; contains does not.
|
|
16
|
+
extraSources: data/employeeColumns.tsx
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Fuzzy by default
|
|
20
|
+
|
|
21
|
+
Typos and skipped characters still match, and while the search is the only thing
|
|
22
|
+
narrowing the grid (no sort, no grouping) the rows are ordered by **match
|
|
23
|
+
quality**, best first.
|
|
24
|
+
|
|
25
|
+
That ordering is derived and never written into `sorting`: no column takes
|
|
26
|
+
`aria-sort`, nothing is written to the persisted slices, and the next sort click
|
|
27
|
+
replaces it.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
const grid = useTMDataGrid({ data, columns, quickSearchMode: "contains" });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`"contains"` restores plain substring matching. An explicit `globalFilterFn`
|
|
34
|
+
overrides both, and switches the rank ordering off with it.
|
|
35
|
+
|
|
36
|
+
`fuzzyGlobalFilterFn` is exported for building your own input over the same
|
|
37
|
+
matching.
|
|
38
|
+
|
|
39
|
+
## Match highlighting
|
|
40
|
+
|
|
41
|
+
Opt in with `enableMatchHighlighting: true` and cells mark the matched part of
|
|
42
|
+
their text, while the quick search is active or a `contains` / `starts with` /
|
|
43
|
+
`ends with` column filter is.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
const grid = useTMDataGrid({ data, columns, enableMatchHighlighting: true });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
What is marked is the **contiguous, case-insensitive** occurrence of the search
|
|
50
|
+
term, so a fuzzy typo-match with no contiguous occurrence is not highlighted.
|
|
51
|
+
Equality operators highlight nothing.
|
|
52
|
+
|
|
53
|
+
**Default-rendered cells only.** A column with its own `cell` renderer is
|
|
54
|
+
excluded: the grid reproduces the default value-to-string render with the marks
|
|
55
|
+
added, and does not modify a custom renderer's output.
|
|
56
|
+
|
|
57
|
+
The mark colour is `--dg-match-highlight-bg`, a yellow that follows the Mantine
|
|
58
|
+
colour scheme.
|
|
59
|
+
|
|
60
|
+
## Opting columns out
|
|
61
|
+
|
|
62
|
+
`enableGlobalFilter: false` on a column takes it out of the search; on the table
|
|
63
|
+
it removes the input entirely. The generated lanes are already excluded.
|
|
64
|
+
|
|
65
|
+
A search input of your own can skip the component and call
|
|
66
|
+
`table.setGlobalFilter` directly. The state is TanStack's own, so a
|
|
67
|
+
[`manualFiltering`](/docs/server-side) grid forwards it to the server, and it is
|
|
68
|
+
one of the persisted `data` slices.
|
|
69
|
+
|
|
70
|
+
## Reference
|
|
71
|
+
|
|
72
|
+
| Name | Kind | Type | Default | What it does |
|
|
73
|
+
| --- | --- | --- | --- | --- |
|
|
74
|
+
| `TMDataGrid.Search` | Component | – | – | The debounced quick-search input. |
|
|
75
|
+
| `placeholder` | Search prop | `string` | `labels.searchPlaceholder` | Input placeholder. |
|
|
76
|
+
| `debounce` | Search prop | `number` | `250` | Pause before the filter applies, in ms. `0` filters per keystroke. |
|
|
77
|
+
| `w` | Search prop | `number \| string` | `220` | Input width. |
|
|
78
|
+
| `quickSearchMode` | Option | `"fuzzy" \| "contains"` | `"fuzzy"` | How the search matches. |
|
|
79
|
+
| `enableMatchHighlighting` | Option | `boolean` | `false` | Mark the matched text in default-rendered cells. |
|
|
80
|
+
| `enableGlobalFilter` | Table option | `boolean` | `true` | Also a column option. `false` removes the input, or one column's participation. |
|
|
81
|
+
| `globalFilterFn` | Table option | filter fn | `fuzzy` | Overrides the matching, and the rank ordering with it. |
|
|
82
|
+
| `fuzzyGlobalFilterFn` | Export | filter fn | – | The default matcher, for a custom input. |
|
|
83
|
+
| `--dg-match-highlight-bg` | CSS variable | colour | Themed yellow | The mark colour. |
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Row details
|
|
2
|
+
|
|
3
|
+
A panel that opens underneath a row, spanning every column, holding whatever
|
|
4
|
+
does not fit in the cells - a summary card, an action strip, a nested table.
|
|
5
|
+
|
|
6
|
+
Setting `renderDetails` turns the lane on. There is no separate flag.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
const grid = useTMDataGrid({
|
|
10
|
+
data,
|
|
11
|
+
columns,
|
|
12
|
+
renderDetails: ({ row }) => <EmployeeCard employee={row.original} />,
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```demo
|
|
17
|
+
file: rows/DetailsPanel.tsx
|
|
18
|
+
hint: Expand a few rows - the panels differ in height, and each one is measured.
|
|
19
|
+
height: 460
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The panel is as tall as whatever it renders. The grid measures each one, so
|
|
23
|
+
they need not be uniform. `renderDetailsEstHeight` (default `160`) is what the
|
|
24
|
+
virtualizer assumes for a panel it has not measured yet, which keeps the
|
|
25
|
+
scrollbar accurate for rows that open off screen.
|
|
26
|
+
|
|
27
|
+
## The details lane
|
|
28
|
+
|
|
29
|
+
Setting `renderDetails` prepends a generated chevron column,
|
|
30
|
+
`DETAILS_COLUMN_ID` (`"__details__"`), pinned to the left after the checkbox
|
|
31
|
+
and tree columns - `[checkbox, tree, details, …]`.
|
|
32
|
+
|
|
33
|
+
It is a system lane: as wide as the chevron it holds, with no resize handle and
|
|
34
|
+
no column menu, and it cannot be hidden, moved, resized or unpinned. Its header
|
|
35
|
+
is a control rather than a title, expanding and collapsing every panel at once.
|
|
36
|
+
|
|
37
|
+
Group rows get no chevron: they expand into their children from the tree lane.
|
|
38
|
+
|
|
39
|
+
## Opening a row from elsewhere
|
|
40
|
+
|
|
41
|
+
Which rows are open is TanStack's own `expanded` state, so anything can open
|
|
42
|
+
one - a context menu item, a double-click, a button in your own cell:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<Menu.Item onClick={() => row.toggleExpanded()}>Show details</Menu.Item>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Anything reading `row.getIsExpanded()` inside a cell has to subscribe to the
|
|
49
|
+
store with TanStack Store's
|
|
50
|
+
[`useSelector`](https://tanstack.com/store/latest/docs/framework/react/reference), or the
|
|
51
|
+
React Compiler caches the call along with the `row` identity and the control
|
|
52
|
+
never updates:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
const expanded = useSelector(row.table.store, () => row.getIsExpanded());
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Because it is the standard state, `table.toggleAllRowsExpanded()`,
|
|
59
|
+
`initialState.expanded` and persistence all work with it. It is one of the
|
|
60
|
+
`data` slices, so open panels survive a reload on a
|
|
61
|
+
[persisted](/docs/use-tm-data-grid#persist) grid.
|
|
62
|
+
|
|
63
|
+
> TanStack resets `expanded` whenever the `data` array changes, which would
|
|
64
|
+
> close every panel on each draft commit. The grid therefore sets
|
|
65
|
+
> `autoResetExpanded: false`; pass `autoResetExpanded: true` to have a new
|
|
66
|
+
> `data` array close the panels.
|
|
67
|
+
|
|
68
|
+
## The two kinds of expanding
|
|
69
|
+
|
|
70
|
+
TanStack keeps one `expanded` state, and the grid uses it for two unrelated
|
|
71
|
+
things: opening a group row into its children, and opening a data row into its
|
|
72
|
+
panel. The controls are kept separate. The details header only opens and closes
|
|
73
|
+
panels, and "Expand all groups" in the tree menu only unfolds the tree.
|
|
74
|
+
|
|
75
|
+
`table.toggleAllRowsExpanded()` is the one that does not distinguish them: it
|
|
76
|
+
writes the state's whole-table form, which is every group and every panel at
|
|
77
|
+
once. `resolveExpandAll` and `areAllRowsExpanded` are exported for anything
|
|
78
|
+
building its own control:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
table.setExpanded(
|
|
82
|
+
resolveExpandAll({
|
|
83
|
+
rows: table.getPrePaginatedRowModel().flatRows,
|
|
84
|
+
expanded: table.store.state.expanded,
|
|
85
|
+
target: "details",
|
|
86
|
+
expand: true,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Panel behaviour
|
|
92
|
+
|
|
93
|
+
The panel is a cell spanning the row, inside the row element. It takes the row's
|
|
94
|
+
background, moves with it and is measured with it, but it is **not a row**:
|
|
95
|
+
`aria-rowcount` still counts records, and a click or right-click inside it does
|
|
96
|
+
not select or highlight the row underneath. It carries
|
|
97
|
+
`data-dg-part="details"` with the row's `data-row-id`. See
|
|
98
|
+
[Testing](/docs/testing).
|
|
99
|
+
|
|
100
|
+
Group rows have no panel. Expanding one opens its children.
|
|
101
|
+
|
|
102
|
+
## Reference
|
|
103
|
+
|
|
104
|
+
| Name | Kind | Type | Default | What it does |
|
|
105
|
+
| --- | --- | --- | --- | --- |
|
|
106
|
+
| `renderDetails` | Option | `({ row, table }) => ReactNode` | – | Contents of the panel. Setting it adds the lane. |
|
|
107
|
+
| `renderDetailsEstHeight` | Option | `number` | `160` | Height the virtualizer assumes for an unmeasured panel. |
|
|
108
|
+
| `initialState.expanded` | Table option | `ExpandedState` | `{}` | Rows open at mount. A data slice, so it persists. |
|
|
109
|
+
| `autoResetExpanded` | Table option | `boolean` | `false` | `true` closes the panels when the `data` array changes. Off by default, so a draft commit keeps them open. |
|
|
110
|
+
| `DETAILS_COLUMN_ID` | Export | `"__details__"` | – | Id of the generated chevron column. |
|
|
111
|
+
| `resolveExpandAll` | Export | `(args) => ExpandedState` | – | Expand or collapse every group, or every panel, but not both. |
|
|
112
|
+
| `areAllRowsExpanded` | Export | `(args) => boolean` | – | Whether every row of one target is open. |
|
|
113
|
+
| `data-dg-part="details"` | Data attribute | – | – | The panel element, carrying the row's `data-row-id`. |
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
| `renderColumnMenuItems` | Table prop | `({ column, table, internalItems }) => ReactNode[]` | – | Contents of a column's menu. An empty list removes the button. |
|
|
146
|
+
| `rowContextMenuProps` | Table prop | `MenuProps` | – | Passed to the Mantine `Menu`. |
|
|
147
|
+
| `TMDataGridCellEventArgs` | Export | type | – | The argument the three cell handlers receive. |
|
|
148
|
+
| `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. |
|