@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.
- package/README.md +5 -212
- package/dist/index.d.ts +1281 -768
- package/dist/index.js +4607 -3250
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/adding-rows.md +132 -0
- package/docs/anatomy.md +119 -0
- package/docs/card-view.md +108 -0
- package/docs/cell-selection.md +194 -0
- package/docs/column-layout.md +182 -0
- package/docs/column-menu.md +66 -0
- package/docs/columns.md +269 -0
- package/docs/components.md +311 -0
- package/docs/draft-store.md +242 -0
- package/docs/editing.md +303 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +319 -0
- package/docs/filtering.md +362 -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/migrating-to-2.md +163 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +114 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +179 -0
- package/docs/quick-search.md +84 -0
- package/docs/row-details.md +115 -0
- package/docs/row-interaction.md +149 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +136 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +112 -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 +744 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +22 -46
- package/skills/appearance/SKILL.md +72 -19
- package/skills/cell-selection/SKILL.md +49 -48
- package/skills/columns/SKILL.md +125 -70
- package/skills/columns/references/columns-api.md +59 -0
- package/skills/data/SKILL.md +112 -18
- package/skills/editing/SKILL.md +76 -42
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +25 -20
- package/skills/editing/references/editors-and-validation.md +80 -18
- package/skills/filtering/SKILL.md +155 -41
- package/skills/getting-started/SKILL.md +116 -16
- package/skills/grouping/SKILL.md +31 -16
- package/skills/migrating-to-2/SKILL.md +244 -0
- package/skills/options/SKILL.md +24 -12
- package/skills/rows/SKILL.md +22 -18
- package/skills/rows/references/rows-api.md +10 -6
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +150 -32
- package/skills/testing-components/SKILL.md +230 -0
- package/skills/testing-editing/SKILL.md +240 -0
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
- 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 +5 -51
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
- 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 +15 -8
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +357 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
- package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -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/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
- 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 +16 -2
- 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/components/generatedColumns.tsx +187 -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}/columnOptions.ts +60 -8
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
- package/src/core/export.ts +704 -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}/grouping.ts +21 -0
- package/src/{tmdatagrid/core → core}/labels.ts +51 -6
- package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
- package/src/core/pageReset.ts +120 -0
- package/src/core/pagination.ts +81 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/{tmdatagrid/core → core}/summary.ts +20 -4
- package/src/{tmdatagrid/index.ts → index.ts} +69 -35
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
- /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}/capabilities.ts +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}/controlledState.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.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}/resizePreview.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
- /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Components and hooks
|
|
2
|
+
|
|
3
|
+
Every hook and component the package exports.
|
|
4
|
+
A part whose props belong to one topic is documented on that topic's page, and listed here with a link; everything else - the root, the Table, and the parts whose props scatter across every topic - is written up in full below.
|
|
5
|
+
The options `useTMDataGrid` takes are on [useTMDataGrid](/docs/use-tm-data-grid).
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { TMDataGrid, useTMDataGrid } from "@jielga/tmdatagrid";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The parts of the grid are reached through the `TMDataGrid` object.
|
|
12
|
+
`TMDataGridSearch`, `TMDataGridFilterPills`, `TMDataGridDraftActions`, the editors and the filter controls are also exported by name; the remaining parts are available only as `TMDataGrid.Part`.
|
|
13
|
+
|
|
14
|
+
## useTMDataGrid
|
|
15
|
+
|
|
16
|
+
Creates the table instance, the UI store and the edit engine.
|
|
17
|
+
Spread its result onto `TMDataGrid`:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
const grid = useTMDataGrid({ data, columns });
|
|
21
|
+
|
|
22
|
+
<TMDataGrid {...grid}>
|
|
23
|
+
<TMDataGrid.Table />
|
|
24
|
+
</TMDataGrid>;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`useTMDataGrid<TData>(options: UseTMDataGridOptions<TData>): TMDataGridApi<TData>`
|
|
28
|
+
|
|
29
|
+
| Field | Type | What it is |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `table` | `TMDataGridTable<TData>` | The TanStack table instance. State lives in `table.store`. |
|
|
32
|
+
| `ui` | `TMDataGridUiStore` | Panels, drag state, focused cell and cell range. |
|
|
33
|
+
| `edit` | `TMDataGridEditApi` | The [editing engine](/docs/editing). Inert until `editing` is set. |
|
|
34
|
+
| `features` | `TMDataGridFeatureFlags` | Feature switches, re-read from the options on every render. |
|
|
35
|
+
| `labels` | `TMDataGridLabels` | The [dictionary](/docs/localization), merged over the English defaults. |
|
|
36
|
+
| `renderDetails` | `TMDataGridDetailsRenderer<TData> \| undefined` | The [details renderer](/docs/row-details), passed through to the Table. |
|
|
37
|
+
| `renderDetailsEstHeight` | `number` | The option, or `160`. |
|
|
38
|
+
| `overscan` | `number` | The option, or `6`. |
|
|
39
|
+
| `resetSettings` | `() => void` | Puts visibility, order, widths, pinning and grouping back to a first visit. |
|
|
40
|
+
| `scrollToRow` | `({ rowId, align? }) => boolean` | Scrolls a row into view. Returns `false` when the row is not in the current view. |
|
|
41
|
+
|
|
42
|
+
## useTMDataGridContext
|
|
43
|
+
|
|
44
|
+
Returns the grid from inside a component rendered under `TMDataGrid`.
|
|
45
|
+
It throws when called outside one.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const { table, labels, controlSize } = useTMDataGridContext();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The value carries every field `useTMDataGrid` returns, plus three resolved from `size`:
|
|
52
|
+
|
|
53
|
+
| Field | Type | What it is |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `size` | `TMDataGridSize` | The `size` prop set on `TMDataGrid`. |
|
|
56
|
+
| `rowHeight` | `number` | Row height in pixels, from `size` and `meta.rowHeight`. |
|
|
57
|
+
| `controlSize` | `TMDataGridSize` | The Mantine control size that pairs with `size`. |
|
|
58
|
+
|
|
59
|
+
## TMDataGrid
|
|
60
|
+
|
|
61
|
+
The root element.
|
|
62
|
+
Its props type is `TMDataGridProps`.
|
|
63
|
+
It provides the grid to every part through context, and takes every field `useTMDataGrid` returns alongside these props:
|
|
64
|
+
|
|
65
|
+
| Prop | Type | Default | Description |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| `children` | `ReactNode` | – | The grid's parts, in render order. |
|
|
68
|
+
| `size` | `TMDataGridSize` | `"md"` | The [size scale](/docs/styling#the-size-scale). |
|
|
69
|
+
| `className` · `style` · `id` | `string` · `CSSProperties` · `string` | – | Set on the root element. Give the grid a bounded height - see [Layout](/docs/styling#layout). |
|
|
70
|
+
| `data-testid` | `string` | – | Names the grid for [tests](/docs/testing). Set it when a page holds more than one grid. |
|
|
71
|
+
|
|
72
|
+
## TMDataGrid.Table
|
|
73
|
+
|
|
74
|
+
The scrollable surface: the header, the virtualized body and the filter panel.
|
|
75
|
+
Pass the row type so the handlers are typed:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Default | Description |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| `onRowClick` | `(row) => void` | – | Called when a body row is clicked. Rows show a pointer cursor when set. See [Clicks and context menus](/docs/row-interaction). |
|
|
84
|
+
| `onCellClick` · `onCellDoubleClick` · `onCellContextMenu` | `(args: TMDataGridCellEventArgs) => void` | – | Called on cell click, double-click and right-click. Receive `{ cell, row, column, event }`. See [Clicks and context menus](/docs/row-interaction). |
|
|
85
|
+
| `renderRowContextMenu` | `TMDataGridRowContextMenuRenderer` | – | Contents of a row's context menu. Receives `{ table, row, cell, close, internalItems }`. See [Context menus](/docs/row-interaction#context-menus). |
|
|
86
|
+
| `rowContextMenuProps` | `Omit<MenuProps, "opened" \| "onChange" \| "children">` | – | Props passed to the Mantine `Menu` behind the context menu. |
|
|
87
|
+
| `renderColumnMenuItems` | `TMDataGridColumnMenuItemsRenderer` | – | Contents of a column's menu. Returning an empty array leaves the column with no menu button. See [Column header menu](/docs/column-menu). |
|
|
88
|
+
| `rowClassName` | `string \| (row) => string` | – | Class for a body row. See [Row styling](/docs/row-styling). |
|
|
89
|
+
| `rowStyle` | `TMDataGridRowStyle \| (row) => TMDataGridRowStyle` | – | Inline style for a body row. Set `--row-bg` rather than `background`. See [Row styling](/docs/row-styling#set-the-row-background). |
|
|
90
|
+
| `striped` | `boolean` | `false` | If set, every second row takes `--dg-row-striped-bg`. See [Striping](/docs/row-styling#striping). |
|
|
91
|
+
| `onScrollToTop` · `onScrollToBottom` · `onScrollToLeft` · `onScrollToRight` | `() => void` | – | Called once on arriving at that edge, not on mount and not per scroll event. See [Edge callbacks](/docs/scrolling#edge-callbacks). |
|
|
92
|
+
| `onReachEnd` | `() => void` | – | Called as the scroll nears the last row, once per row count. Sorting and filtering must be server-side. See [Infinite scroll](/docs/server-side#infinite-scroll). |
|
|
93
|
+
| `reachEndThreshold` | `number` | `10` | Rows before the end at which `onReachEnd` fires. |
|
|
94
|
+
| `renderEmptyState` | `({ hasActiveFilters, table }) => ReactNode` | – | Replaces both built-in empty messages. See [Loading and empty states](/docs/loading-and-empty). |
|
|
95
|
+
| `aria-label` · `aria-labelledby` | `string` | – | The grid's accessible name, announced on entry and matched by `getByRole("grid", { name })`. |
|
|
96
|
+
|
|
97
|
+
Note that `onReachEnd` and `enablePagination` slice the same scroll: the pager caps the rows, so the end reached is the page's.
|
|
98
|
+
Setting both logs a warning.
|
|
99
|
+
|
|
100
|
+
## TMDataGrid.Toolbar
|
|
101
|
+
|
|
102
|
+
A flex row above the grid. See [Toolbar](/docs/toolbar).
|
|
103
|
+
|
|
104
|
+
| Prop | Type | Default | Description |
|
|
105
|
+
| --- | --- | --- | --- |
|
|
106
|
+
| `children` | `ReactNode` | – | The toolbar's contents, in render order. |
|
|
107
|
+
| `withBottomBorder` | `boolean` | `false` | A 1px bottom border in the theme's default border colour, the line the header draws under itself. |
|
|
108
|
+
| Mantine `BoxProps` | | – | Style props (`mb`, `px`, `hiddenFrom`, …), `className`, `style` and `mod`, set on the row. |
|
|
109
|
+
|
|
110
|
+
## TMDataGrid.Spacer
|
|
111
|
+
|
|
112
|
+
Pushes the toolbar items after it to the right.
|
|
113
|
+
|
|
114
|
+
| Prop | Type | Default | Description |
|
|
115
|
+
| --- | --- | --- | --- |
|
|
116
|
+
| Mantine `BoxProps` | | – | Style props, `className` and `style`, set on the spacer. |
|
|
117
|
+
|
|
118
|
+
## TMDataGrid.Search
|
|
119
|
+
|
|
120
|
+
A debounced quick-search input that writes the grid's global filter.
|
|
121
|
+
It renders nothing when no column is searchable. See [Quick search](/docs/quick-search).
|
|
122
|
+
|
|
123
|
+
| Prop | Type | Default | Description |
|
|
124
|
+
| --- | --- | --- | --- |
|
|
125
|
+
| `placeholder` | `string` | `labels.searchPlaceholder` | Placeholder text. |
|
|
126
|
+
| `debounce` | `number` | `250` | Milliseconds between the last keystroke and the filter being written. |
|
|
127
|
+
| `w` | `number \| string` | `220` | Width of the input. |
|
|
128
|
+
|
|
129
|
+
## The filter parts
|
|
130
|
+
|
|
131
|
+
Filtering owns these three, so their props are on that page rather than repeated here.
|
|
132
|
+
|
|
133
|
+
| Component | What it is | Props |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `TMDataGrid.FilterButton` | Toggles the filter surface, tinted with the count of active filters. Renders nothing when no column can be filtered, and nothing under `filters.surface: "none"`. | None. [Filtering](/docs/filtering#tmdatagridfilterbutton) |
|
|
136
|
+
| `TMDataGrid.FilterPanel` | The filter rows, as a plain block with no title, no close button and no open state - the popup and sidebar [surfaces](/docs/filtering#the-filters-option) wrap it. | `layout`, Mantine `BoxProps`. [Filtering](/docs/filtering#tmdatagridfilterpanel) |
|
|
137
|
+
| `TMDataGrid.FilterPills` · `TMDataGridFilterPills` | One pill per active filter. Takes the grid as an `api` prop rather than from context, so it renders anywhere on the page. | `api`, `size`, `showClearAll`, `onPillClick`, `className`. [Filtering](/docs/filtering#tmdatagridfilterpills) |
|
|
138
|
+
|
|
139
|
+
`openColumnFilter(api, columnId)` sends the user to a column's filter control; it is documented with them, on [Filtering](/docs/filtering#opencolumnfilter).
|
|
140
|
+
|
|
141
|
+
## TMDataGrid.Menu
|
|
142
|
+
|
|
143
|
+
The burger at the end of the toolbar and the Mantine `Menu` it opens.
|
|
144
|
+
Its children are the dropdown. See [Grid menu](/docs/menu).
|
|
145
|
+
|
|
146
|
+
| Prop | Type | Default | Description |
|
|
147
|
+
| --- | --- | --- | --- |
|
|
148
|
+
| `children` | `ReactNode` | – | The dropdown: your own `Menu.Item`s and the `TMDataGrid.Menu.*` items. |
|
|
149
|
+
| `icon` | `ReactNode` | The burger | The trigger's icon. |
|
|
150
|
+
| `label` | `string` | `labels.menuButton` | The trigger's tooltip and `aria-label`. |
|
|
151
|
+
| Mantine `MenuProps` | | `position="bottom-end"`, `shadow="md"`, `width={260}`, `withinPortal` | Passed to the `Menu`. |
|
|
152
|
+
|
|
153
|
+
`TMDataGrid.Menu.Columns`, `.ColumnToggles`, `.ShowHideAll` and `.ResetLayout` are the column chooser as menu items, for this menu or any Mantine `Menu` inside the grid.
|
|
154
|
+
`Columns` takes `searchable` (default `"auto"`, a search box from six columns), `ColumnToggles` takes `search`; the other two take no props.
|
|
155
|
+
|
|
156
|
+
## TMDataGrid.ColumnsPanel
|
|
157
|
+
|
|
158
|
+
The column chooser as plain controls: a checkbox list of the hideable columns, a search box once there are six of them, a show-all toggle and a reset button.
|
|
159
|
+
For a Popover, a Drawer or an inline layout; `TMDataGrid.Menu.Columns` is the same thing as menu items.
|
|
160
|
+
|
|
161
|
+
| Prop | Type | Default | Description |
|
|
162
|
+
| --- | --- | --- | --- |
|
|
163
|
+
| `searchable` | `boolean \| "auto"` | `"auto"` | The search box over the list. `"auto"` shows it from six columns, `true` always, `false` never. |
|
|
164
|
+
| Mantine `BoxProps` | | – | Style props (`w`, `p`, …), `className` and `style`, set on the panel block. |
|
|
165
|
+
|
|
166
|
+
## TMDataGrid.LoadingIndicator
|
|
167
|
+
|
|
168
|
+
A spinner, shown while `meta.loading` is `true`. See [Loading and empty states](/docs/loading-and-empty).
|
|
169
|
+
|
|
170
|
+
No props.
|
|
171
|
+
|
|
172
|
+
## TMDataGrid.SummaryCount
|
|
173
|
+
|
|
174
|
+
The row count, as shown over total.
|
|
175
|
+
The total comes from `meta.totalRowCount` when set, and from the unfiltered row count otherwise.
|
|
176
|
+
While a column is grouped the shown count includes the group rows and the total keeps counting records, so `42 / 42` reads `48 / 42` under six groups; pass `children` to show a count of your own.
|
|
177
|
+
|
|
178
|
+
| Prop | Type | Default | Description |
|
|
179
|
+
| --- | --- | --- | --- |
|
|
180
|
+
| `children` | `ReactNode` | – | Replaces the count with your own text. |
|
|
181
|
+
|
|
182
|
+
## TMDataGrid.DraftActions
|
|
183
|
+
|
|
184
|
+
Save and Discard for pending edits.
|
|
185
|
+
It renders nothing unless `editing` is set. See [Editing](/docs/editing).
|
|
186
|
+
|
|
187
|
+
| Prop | Type | Default | Description |
|
|
188
|
+
| --- | --- | --- | --- |
|
|
189
|
+
| `renderActions` | `(args: TMDataGridDraftActionsSlotArgs) => ReactNode` | – | Replaces both buttons. Receives `{ state, actions, Controls }`. |
|
|
190
|
+
|
|
191
|
+
The slot argument carries the state, the operations and the built-in pieces:
|
|
192
|
+
|
|
193
|
+
| Field | Type | What it is |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| `state.draftCount` | `number` | Rows in the draft store: committed edits, committed entry rows and deletion marks. This is what Save sends. |
|
|
196
|
+
| `state.openCount` | `number` | Rows still open, so not part of the save. |
|
|
197
|
+
| `state.openRowIds` | `ReadonlyArray<string>` | The ids behind `openCount`, in the order the grid opened them. An entered row appears as its `tempId`. |
|
|
198
|
+
| `state.isSubmitting` | `boolean` | `true` while any open row is submitting. Not the save; see `state.isSaving`. |
|
|
199
|
+
| `state.isSaving` | `boolean` | `true` while `saveDrafts` is in flight, until the consumer's callbacks settle. The built-in Save shows it as its loading state. |
|
|
200
|
+
| `actions.save` | `() => Promise<TMDataGridSaveDraftsResult>` | Sends the draft store - `edit.saveDrafts()`. Open rows are left alone. Resolves `{ ok, saved, kept, reopened }`. |
|
|
201
|
+
| `actions.commitAll` | `() => Promise<TMDataGridCommitAllResult>` | Submits every open row, committing the ones that validate - `edit.commitAll()`. Resolves `{ ok, committed, open }`. |
|
|
202
|
+
| `actions.discard` | `() => void` | Drops open form state and the draft store alike. |
|
|
203
|
+
| `actions.scrollToRow` | `({ rowId, align? }) => boolean` | [`scrollToRow`](/docs/scrolling#scrolling-to-a-row), passed through. |
|
|
204
|
+
| `actions.scrollToFirstOpenRow` | `(align?) => boolean` | Scrolls to the first open row in display order. `false` when none could be reached. |
|
|
205
|
+
| `Controls.Save` · `Controls.Discard` · `Controls.OpenRowsNote` | `() => ReactNode` | The built-in pieces, to place in your own layout. |
|
|
206
|
+
|
|
207
|
+
The two orderings differ: `openRowIds` is the order the grid opened the rows,
|
|
208
|
+
while `scrollToFirstOpenRow` takes "first" in display order, so it follows the
|
|
209
|
+
current sort, filter and page. `openRowIds[0]` need not be the row it reaches.
|
|
210
|
+
|
|
211
|
+
## TMDataGrid.Footer
|
|
212
|
+
|
|
213
|
+
The pager bar: rows per page, the current range, and previous and next.
|
|
214
|
+
It renders nothing when pagination is off. See [Pagination](/docs/pagination).
|
|
215
|
+
|
|
216
|
+
| Prop | Type | Default | Description |
|
|
217
|
+
| --- | --- | --- | --- |
|
|
218
|
+
| `pageSizeOptions` | `ReadonlyArray<number>` | `[10, 25, 50, 100]` | The choices in the rows-per-page select. |
|
|
219
|
+
| `renderPagination` | `(args: TMDataGridPaginationSlotArgs) => ReactNode` | – | Replaces the pager. Receives `{ state, actions, Controls }`. |
|
|
220
|
+
| Mantine `BoxProps` | | – | Style props (`mt`, `px`, …), `className` and `style`, set on the footer bar. |
|
|
221
|
+
|
|
222
|
+
The slot argument holds the paging state, its operations and the built-in controls:
|
|
223
|
+
|
|
224
|
+
| Field | Type | What it is |
|
|
225
|
+
| --- | --- | --- |
|
|
226
|
+
| `state` | `TMDataGridPaginationState` | `pageIndex`, `pageSize`, `pageCount`, `rowCount`, `canPreviousPage`, `canNextPage`, `isPagingActive`, and the 1-based `from` and `to`. |
|
|
227
|
+
| `actions` | `TMDataGridPaginationActions` | `setPageIndex`, `setPageSize`, `previousPage`, `nextPage`, `firstPage`, `lastPage`. |
|
|
228
|
+
| `Controls.PageSize` · `Controls.Range` · `Controls.Pager` | `() => ReactNode` | The three built-in controls, to place in your own layout. |
|
|
229
|
+
|
|
230
|
+
`pageCount` is `-1` when a manual grid declares an unknown total, and `isPagingActive` is `false` while a grouping suspends paging.
|
|
231
|
+
`getTMDataGridPaginationApi(table, isPaging?)` returns the same `state` and `actions` for a pager built outside the grid.
|
|
232
|
+
|
|
233
|
+
## Editors
|
|
234
|
+
|
|
235
|
+
The control a cell opens for editing.
|
|
236
|
+
The grid picks one from the column's `meta.type`, and `meta.edit.editor` replaces it. See [Editors and validation](/docs/editors).
|
|
237
|
+
|
|
238
|
+
| Component | Renders | Used for `meta.type` |
|
|
239
|
+
| --- | --- | --- |
|
|
240
|
+
| `TMDataGridStringEditor` | Mantine `TextInput` | `"string"` |
|
|
241
|
+
| `TMDataGridNumberEditor` | `NumberInput`, writing `null` for an empty cell | `"number"` |
|
|
242
|
+
| `TMDataGridBooleanEditor` | `Checkbox` | `"boolean"` |
|
|
243
|
+
| `TMDataGridDateEditor` | `TextInput` with `type="date"` | `"date"` |
|
|
244
|
+
| `TMDataGridSelectEditor` | `Select`, committing on pick in `"cell"` mode | `"select"` |
|
|
245
|
+
| `TMDataGridMultiSelectEditor` | `MultiSelect`, committing on Enter or blur | `"multiSelect"` |
|
|
246
|
+
|
|
247
|
+
Every editor takes the same argument object, `TMDataGridEditorArgs`, so a custom editor can wrap a built-in rather than replace it:
|
|
248
|
+
|
|
249
|
+
| Field | Type | What it is |
|
|
250
|
+
| --- | --- | --- |
|
|
251
|
+
| `field` | `TMDataGridEditField` | The TanStack Form field for this cell. |
|
|
252
|
+
| `form` | `TMDataGridRowEditForm` | The whole row form, for an editor that reads sibling fields. |
|
|
253
|
+
| `cell` · `row` · `column` · `table` | TanStack instances | The cell being edited and its surroundings. |
|
|
254
|
+
| `commit` | `() => Promise<boolean>` | Commits the edit, as Enter would. |
|
|
255
|
+
| `cancel` | `() => void` | Drops the draft, as Escape would. |
|
|
256
|
+
| `size` | `TMDataGridSize` | The grid's resolved control size. |
|
|
257
|
+
| `seedText` | `string \| undefined` | Set when typing opened the editor. |
|
|
258
|
+
|
|
259
|
+
## Filter controls
|
|
260
|
+
|
|
261
|
+
The value control in a filter row.
|
|
262
|
+
The grid picks one from the column's `meta.type` and the current operator, and `meta.filter.control` replaces it. See [Filtering](/docs/filtering).
|
|
263
|
+
|
|
264
|
+
| Component | Renders | Suits |
|
|
265
|
+
| --- | --- | --- |
|
|
266
|
+
| `TMDataGridFilterValueInput` | A text input, a multi-select, a boolean select or a from/to pair, by operator | Every column. This is the default. |
|
|
267
|
+
| `DgRangeSliderFilter` | `RangeSlider` bounded by the values in the data | `number` columns with `between` |
|
|
268
|
+
| `DgDateRangeFilter` | Two `type="date"` inputs | `date` columns with `between` |
|
|
269
|
+
| `DgAutocompleteFilter` | `Autocomplete` over the column's values | Text columns with a scalar operator |
|
|
270
|
+
| `DgTriStateFilter` | `SegmentedControl` of all, true and false | `boolean` columns |
|
|
271
|
+
|
|
272
|
+
Each of the four specialised controls falls back to `TMDataGridFilterValueInput` for the operators it does not handle.
|
|
273
|
+
Every control takes the same argument object, `TMDataGridFilterControlArgs`:
|
|
274
|
+
|
|
275
|
+
| Field | Type | What it is |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| `column` · `table` | TanStack instances | The column being filtered, and the table. |
|
|
278
|
+
| `operator` | `TMDataGridFilterOperator` | The row's selected operator. A control reads it and never writes it. |
|
|
279
|
+
| `value` | `string \| ReadonlyArray<string>` | The bare filter value, never the `{ operator, value }` wrapper. |
|
|
280
|
+
| `onChange` | `(next: string \| ReadonlyArray<string>) => void` | Writes the bare value. The grid pairs it with the current operator. |
|
|
281
|
+
| `options` | `ReadonlyArray<TMDataGridOption>` | The column's options, resolved for a column declaring `meta.options` or a select type. Empty otherwise. |
|
|
282
|
+
| `size` | `TMDataGridSize` | The grid's resolved control size. |
|
|
283
|
+
| `labels` | `TMDataGridLabels` | The resolved dictionary. |
|
|
284
|
+
|
|
285
|
+
## Reference
|
|
286
|
+
|
|
287
|
+
| Name | Kind | Type | Default | What it does |
|
|
288
|
+
| --- | --- | --- | --- | --- |
|
|
289
|
+
| `useTMDataGrid` | Hook | `(options) => TMDataGridApi` | – | Creates the table, UI store and edit engine. |
|
|
290
|
+
| `useTMDataGridContext` | Hook | `() => TMDataGridContextValue` | – | The grid, from inside any part. Throws outside `TMDataGrid`. |
|
|
291
|
+
| `TMDataGrid` | Component | – | – | The root element. Provides the grid through context. |
|
|
292
|
+
| `TMDataGridProps` | Type | – | – | The props of `TMDataGrid`: the fields of `TMDataGridApi`, plus `children`, `size`, `className`, `style`, `id` and `data-testid`. |
|
|
293
|
+
| `TMDataGrid.Table` | Component | – | – | Header, virtualized body and filter panel. |
|
|
294
|
+
| `TMDataGridTableProps` | Type | – | – | The props of `TMDataGrid.Table`. |
|
|
295
|
+
| `TMDataGrid.Toolbar` · `.Spacer` | Components | – | – | The toolbar row, and the gap that pushes items right. |
|
|
296
|
+
| `TMDataGridToolbarProps` | Type | – | – | The props of `TMDataGrid.Toolbar`. |
|
|
297
|
+
| `TMDataGrid.Search` | Component | – | – | Quick search input. Also `TMDataGridSearch`. |
|
|
298
|
+
| `TMDataGridSearchProps` | Type | – | – | The props of `TMDataGrid.Search`. |
|
|
299
|
+
| `TMDataGrid.FilterButton` · `.FilterPanel` | Components | – | – | The filter UI. Props on [Filtering](/docs/filtering). |
|
|
300
|
+
| `TMDataGrid.Menu` · `.ColumnsPanel` | Components | – | – | The burger menu, and the column chooser as plain controls. |
|
|
301
|
+
| `TMDataGridColumnsPanelProps` · `TMDataGridColumnSearchable` | Types | – | – | The props of `TMDataGrid.ColumnsPanel`, and the type of the `searchable` prop it shares with `TMDataGrid.Menu.Columns`. |
|
|
302
|
+
| `TMDataGrid.LoadingIndicator` · `.SummaryCount` | Components | – | – | Fetch spinner, and the row count. |
|
|
303
|
+
| `TMDataGrid.DraftActions` | Component | – | – | Save and Discard. Also `TMDataGridDraftActions`. |
|
|
304
|
+
| `TMDataGridDraftActionsProps` | Type | – | – | The props of `TMDataGrid.DraftActions`. |
|
|
305
|
+
| `TMDataGridDraftActionsState` · `TMDataGridDraftActionsActions` · `TMDataGridDraftActionsControls` | Types | – | – | The `state`, `actions` and `Controls` of `TMDataGridDraftActionsSlotArgs`. |
|
|
306
|
+
| `TMDataGrid.Footer` | Component | – | – | The pager bar. |
|
|
307
|
+
| `TMDataGridFooterProps` | Type | – | – | The props of `TMDataGrid.Footer`. |
|
|
308
|
+
| `TMDataGrid.FilterPills` | Component | – | – | Active filters as pills. Also `TMDataGridFilterPills`. Props on [Filtering](/docs/filtering). |
|
|
309
|
+
| `openColumnFilter` | Export | `(api, columnId) => void` | – | Sends the user to a column's filter control. See [Filtering](/docs/filtering#opencolumnfilter). |
|
|
310
|
+
| `getTMDataGridPaginationApi` | Function | `(table, isPaging?) => TMDataGridPaginationApi` | `isPaging`: `true` | Paging state and actions for a pager of your own. |
|
|
311
|
+
| `DEFAULT_EXPORT_OPTIONS` | Constant | `TMDataGridExportSettings` | – | The export defaults `exportOptions` merges over. See [Export](/docs/export). |
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Draft store
|
|
2
|
+
|
|
3
|
+
The draft store holds committed rows inside the grid until the user presses Save.
|
|
4
|
+
It is turned on with `editing.draft`; see [Editing](/docs/editing) for the modes and the rest of the `editing` option.
|
|
5
|
+
|
|
6
|
+
`draft: true` changes where a commit goes, and nothing else.
|
|
7
|
+
Instead of reaching `onCommit`, the row is committed into the grid's draft store, where it waits for `edit.saveDrafts()`.
|
|
8
|
+
The mode still decides what counts as a commit: `{ mode: "row", draft: true }` commits a whole row from the lane's ✓, and `{ mode: "cell", draft: true }` commits a row as the caret leaves the cell.
|
|
9
|
+
|
|
10
|
+
`draft: true` requires `getRowId`.
|
|
11
|
+
The usual setup adds `onSaveDrafts`, which receives the whole store in one call when the user presses Save, and `TMDataGrid.DraftActions` in the toolbar, which renders Save and Discard:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const grid = useTMDataGrid({
|
|
15
|
+
data,
|
|
16
|
+
columns,
|
|
17
|
+
getRowId: (row) => String(row.id),
|
|
18
|
+
editing: {
|
|
19
|
+
mode: "row",
|
|
20
|
+
draft: true,
|
|
21
|
+
onSaveDrafts: async ({ updated, created, deleted }) => {
|
|
22
|
+
await api.saveBatch({ updated, created, deleted });
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
<TMDataGrid {...grid}>
|
|
28
|
+
<TMDataGrid.Toolbar>
|
|
29
|
+
<TMDataGrid.DraftActions />
|
|
30
|
+
</TMDataGrid.Toolbar>
|
|
31
|
+
<TMDataGrid.Table />
|
|
32
|
+
</TMDataGrid>;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
In this demo:
|
|
36
|
+
|
|
37
|
+
- ✓ commits the row into the draft store, and the Backend panel logs nothing
|
|
38
|
+
- Save sends the whole store as one `onSaveDrafts` call
|
|
39
|
+
- with "Reject Sales rows" on, the backend refuses those rows and they keep their drafts
|
|
40
|
+
- "Go to open row" scrolls to a row left open
|
|
41
|
+
|
|
42
|
+
```demo
|
|
43
|
+
file: editing/DraftEditing.tsx
|
|
44
|
+
hint: Double-click a row, ✓ commits it, Save sends the store.
|
|
45
|
+
height: 440
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Saving the store
|
|
49
|
+
|
|
50
|
+
`edit.saveDrafts()` sends the draft store and leaves open rows alone.
|
|
51
|
+
With `onSaveDrafts` set, it makes one call with the whole store, `onSaveDrafts({ updated, created, deleted })`:
|
|
52
|
+
|
|
53
|
+
- `updated` - one entry per committed edit, in the shape `onCommit` receives: `{ rowId, value, original, changes, source }`
|
|
54
|
+
- `created` - one `{ tempId, value }` per committed new row
|
|
55
|
+
- `deleted` - the ids of the rows marked for deletion
|
|
56
|
+
|
|
57
|
+
Without `onSaveDrafts`, `saveDrafts` makes one call per row instead: `onCommit` for each edit, `onRowAdd` for each new row and `onRowDelete` for each deletion.
|
|
58
|
+
`draft: true` without `onSaveDrafts` is therefore valid.
|
|
59
|
+
|
|
60
|
+
`changes` is a list of descriptors, not a patch object; spreading it into a row compiles and writes nothing.
|
|
61
|
+
To build a patch:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
const patch = Object.fromEntries(entry.changes.map((c) => [c.field, c.next]));
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Before the rows are sent, `editing.tableValidators` run once more over every committed row.
|
|
68
|
+
Column rules and `rowValidators` ran at commit on the same values, so they do not run again.
|
|
69
|
+
A row the table rules reject is reopened with its errors and left out of the save.
|
|
70
|
+
On the per-row path, a row whose `onCommit` or `onRowAdd` rejects is reopened the same way.
|
|
71
|
+
|
|
72
|
+
`saveDrafts()` resolves a `TMDataGridSaveDraftsResult`, `{ ok, saved, kept, reopened }`.
|
|
73
|
+
Every id the save took from the draft store is in exactly one list: row ids for edits and deletions, temp ids for new rows, all kinds mixed.
|
|
74
|
+
|
|
75
|
+
- `saved` - left the draft store; the consumer accepted it
|
|
76
|
+
- `kept` - still in the draft store, still committed, and sent again by the next save; see [Saving part of the store](#saving-part-of-the-store)
|
|
77
|
+
- `reopened` - out of the draft store and open again with an error: a table rule rejected it, or on the per-row path its `onCommit` or `onRowAdd` rejected
|
|
78
|
+
- `ok` - `true` when `kept` and `reopened` are both empty
|
|
79
|
+
|
|
80
|
+
On the per-row path a deletion always leaves the store, so it is reported in `saved`.
|
|
81
|
+
An empty store resolves `{ ok: true, saved: [], kept: [], reopened: [] }` without calling the consumer.
|
|
82
|
+
A call made while a save is in flight joins that save and resolves the same result.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
const result = await grid.edit.saveDrafts();
|
|
86
|
+
if (!result.ok) notify(`${result.reopened.length} rows need attention`);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`TMDataGrid.DraftActions` renders the whole-grid controls: Save with the count of rows in the store, Discard, and a note counting the rows still open.
|
|
90
|
+
Save is disabled while the store is empty, however much is being typed, and shows a loading state while the save is in flight.
|
|
91
|
+
The toolbar is declarative: include the component yourself when the grid runs a draft store, or call `edit.saveDrafts()` from a control of your own.
|
|
92
|
+
Without `draft: true` there is nothing to save and Save stays disabled.
|
|
93
|
+
|
|
94
|
+
## Saving part of the store
|
|
95
|
+
|
|
96
|
+
`onSaveDrafts` decides how much of the store is cleared by what it returns:
|
|
97
|
+
|
|
98
|
+
| Returned | Effect |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| nothing | Everything saved. The store is cleared. |
|
|
101
|
+
| a rejected promise, or a throw | Nothing saved. Every draft is kept. |
|
|
102
|
+
| `{ updated, created, deleted }` | The ids reported `false` are kept; the rest are cleared. |
|
|
103
|
+
|
|
104
|
+
Each key takes `false` for the whole bucket, or a map from id to result.
|
|
105
|
+
An id the map does not name counts as saved.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
onSaveDrafts: async ({ updated, created, deleted }) => {
|
|
109
|
+
const failed = await api.saveBatch({ updated, created, deleted });
|
|
110
|
+
return { updated: Object.fromEntries(failed.map((id) => [id, false])) };
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
A kept row stays committed rather than reopening, so the next `saveDrafts()` retries it with the values it already holds.
|
|
115
|
+
`saveDrafts()` reports every id `onSaveDrafts` returned as failed in `kept`, and every id it was sent when it threw, with `ok: false`.
|
|
116
|
+
Without `onSaveDrafts`, a deletion whose `onRowDelete` throws keeps its mark and is reported in `kept` the same way.
|
|
117
|
+
A kept row carries the same markers as every other draft and nothing more; see [Styling pending rows](/docs/editing#styling-pending-rows).
|
|
118
|
+
|
|
119
|
+
## Rows left open
|
|
120
|
+
|
|
121
|
+
A row left open is neither lost nor sent.
|
|
122
|
+
It keeps everything typed into it, stays open across a save, and joins the next save once it is committed.
|
|
123
|
+
`edit.commitAll()` submits every open row at once; rows that fail validation stay open with their errors.
|
|
124
|
+
It resolves a `TMDataGridCommitAllResult`, `{ ok, committed, open }`: every row that was open at the call is in exactly one of the two lists, and `ok` is `true` when `open` is empty.
|
|
125
|
+
"Commit everything, then save" is `commitAll()` followed by `saveDrafts()`:
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
const { ok, open } = await grid.edit.commitAll();
|
|
129
|
+
if (ok) await grid.edit.saveDrafts();
|
|
130
|
+
else grid.scrollToRow({ rowId: open[0]! });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The note beside Save counts the open rows.
|
|
134
|
+
On a long grid the open row may be far from the viewport, and because the grid is [virtualized](/docs/scrolling) it may have no element to scroll to.
|
|
135
|
+
`actions.scrollToFirstOpenRow(align?)` scrolls to the first open row in display order and returns whether it could be reached.
|
|
136
|
+
|
|
137
|
+
`renderActions` replaces the built-in pair and receives its pieces:
|
|
138
|
+
|
|
139
|
+
- `state` - `draftCount`, `openCount`, `openRowIds`, `isSubmitting` and `isSaving`
|
|
140
|
+
- `actions` - `save`, `commitAll`, `discard`, `scrollToRow` and `scrollToFirstOpenRow`
|
|
141
|
+
- `Controls` - `Save`, `Discard` and `OpenRowsNote`, the built-in pieces
|
|
142
|
+
|
|
143
|
+
The full list is on [Components](/docs/components#tmdatagriddraftactions).
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<TMDataGrid.DraftActions
|
|
147
|
+
renderActions={({ state, actions, Controls }) => (
|
|
148
|
+
<Group>
|
|
149
|
+
{state.draftCount > 0 && <Badge>{state.draftCount} ready</Badge>}
|
|
150
|
+
<Button
|
|
151
|
+
disabled={state.openCount === 0}
|
|
152
|
+
onClick={() => {
|
|
153
|
+
actions.scrollToFirstOpenRow("center");
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
Go to open row
|
|
157
|
+
</Button>
|
|
158
|
+
<Controls.OpenRowsNote />
|
|
159
|
+
<Controls.Save />
|
|
160
|
+
<Controls.Discard />
|
|
161
|
+
</Group>
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`state.openRowIds` lists the open rows in the order the grid opened them, for a control the grid does not offer, such as a list or a next-open-row cycle.
|
|
167
|
+
`scrollToFirstOpenRow` takes "first" in display order, so the two need not name the same row.
|
|
168
|
+
An entered row appears as its `tempId`; entry rows are always on screen in the entry block, so the scroll returns `true` without moving.
|
|
169
|
+
|
|
170
|
+
## Unsaved changes
|
|
171
|
+
|
|
172
|
+
`hasPendingEdits(state)` returns `true` while the grid holds anything that has not been saved:
|
|
173
|
+
|
|
174
|
+
- an open row with a value that differs from its original
|
|
175
|
+
- an entry row, open or committed
|
|
176
|
+
- a committed row or a deletion mark in the draft store
|
|
177
|
+
- a save in flight (`isSaving`)
|
|
178
|
+
|
|
179
|
+
A row that is only opened, or whose values were changed back to the original, does not count.
|
|
180
|
+
After a save, a row the save kept, or a row reopened with an error, still counts.
|
|
181
|
+
|
|
182
|
+
The function is a selector over the edit state.
|
|
183
|
+
With `useSelector`, the component re-renders only when the result changes:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { useSelector } from "@tanstack/react-store";
|
|
187
|
+
import { hasPendingEdits } from "@jielga/tmdatagrid";
|
|
188
|
+
|
|
189
|
+
const hasUnsaved = useSelector(grid.edit.store, hasPendingEdits);
|
|
190
|
+
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (!hasUnsaved) return;
|
|
193
|
+
const onBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
194
|
+
event.preventDefault();
|
|
195
|
+
};
|
|
196
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
197
|
+
return () => {
|
|
198
|
+
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
199
|
+
};
|
|
200
|
+
}, [hasUnsaved]);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A router's navigation blocker can pass `hasUnsaved` in the same way, or call `hasPendingEdits(grid.edit.state)` when navigation starts.
|
|
204
|
+
It works in every edit mode.
|
|
205
|
+
Without `draft: true` the draft store stays empty, so only open rows count.
|
|
206
|
+
|
|
207
|
+
## How a committed row behaves
|
|
208
|
+
|
|
209
|
+
A committed row holds no form: its values are data in the draft store.
|
|
210
|
+
To the table it is a row like any other, and everything that reads a row reads the draft:
|
|
211
|
+
|
|
212
|
+
- sorting, filtering, quick search and grouping
|
|
213
|
+
- `aggregatedCell`, `footer` and the faceted filter options
|
|
214
|
+
- export, row selection, the row numbers and counts
|
|
215
|
+
- `edit.getRows()` and `editing.tableValidators`
|
|
216
|
+
- the row callbacks `onRowClick`, `renderRowContextMenu`, `renderDetails`, `isRowEditable`, `meta.edit.enabled`, `rowClassName`, `rowStyle` and `enableRowPinning`, which receive a row whose `original` is the committed draft; an entered row is a record under its temp id, with no server id
|
|
217
|
+
|
|
218
|
+
`data` itself is never modified, and `getRowCount()` with a `rowCount` you set does not grow.
|
|
219
|
+
Only top-level rows are overlaid; children reached through `getSubRows` keep their `data` values.
|
|
220
|
+
A committed row that stops matching a filter or the quick search leaves the view, and Save still counts it.
|
|
221
|
+
|
|
222
|
+
Reopening a committed row, through `begin` or a write with `setCellValue`, `setRowValues` or `clearCell`, builds a fresh form seeded from the committed values and takes the row out of the store until it commits again.
|
|
223
|
+
The row keeps its place in the sort until it commits again or is cancelled.
|
|
224
|
+
|
|
225
|
+
A commit moves nothing else: the page stays, and open details panels and groups stay open.
|
|
226
|
+
TanStack's `autoResetPageIndex` and `autoResetExpanded` fire on any change to the `data` array, which under `draft: true` is every commit, so the grid switches both off and resets the page on a query change itself; see `resetPageOnQueryChange`.
|
|
227
|
+
|
|
228
|
+
A refetch that no longer returns a row drops that row's draft, its open editor and its deletion mark.
|
|
229
|
+
Under `manualPagination` or `manualFiltering` a row missing from `data` is on another page, not gone, so its draft is kept until Save.
|
|
230
|
+
|
|
231
|
+
## Reference
|
|
232
|
+
|
|
233
|
+
| Name | Kind | Type | Default | What it does |
|
|
234
|
+
| ----------------------------- | -------------- | ------------------------------------------------ | ----------------- | ------------------------------------------------------------------------------------------------ |
|
|
235
|
+
| `editing.onSaveDrafts` | Callback | `({ updated, created, deleted }) => void \| Result \| Promise` | – | `draft: true` only. One call for the whole draft store. See [Saving part of the store](#saving-part-of-the-store). |
|
|
236
|
+
| `TMDataGridSaveDraftsArgs` | Type | `{ updated, created, deleted }` | – | What `onSaveDrafts` receives. |
|
|
237
|
+
| `TMDataGridSaveDraftsResponse` | Type | `{ updated?, created?, deleted? }` | – | What `onSaveDrafts` may return to save part of the store. See [Saving part of the store](#saving-part-of-the-store). |
|
|
238
|
+
| `TMDataGridSaveOutcomes` | Type | `boolean \| Record<string, boolean>` | – | One bucket of what `onSaveDrafts` returns. `false` keeps an entry's draft; an id the map does not name counts as saved. |
|
|
239
|
+
| `TMDataGrid.DraftActions` | Component | – | – | Save and Discard for pending edits. |
|
|
240
|
+
| `DraftActions` `renderActions` | Slot | `({ state, actions, Controls }) => ReactNode` | Built-in pair | Replaces the buttons, and hands over their pieces. See [Components](/docs/components#tmdatagriddraftactions). |
|
|
241
|
+
| `actions.scrollToFirstOpenRow` | Slot action | `(align?) => boolean` | `align: "auto"` | Scrolls to the first open row in display order. `false` when none could be reached. |
|
|
242
|
+
| `hasPendingEdits` | Export | `(state) => boolean` | – | Whether the grid holds unsaved work. See [Unsaved changes](#unsaved-changes). |
|