@jielga/tmdatagrid 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -157,6 +157,10 @@ meta: {
157
157
  }
158
158
  ```
159
159
 
160
+ The map receives `TMDataGridEditValueMapArgs`: `{ value, previous, row, column, table }`.
161
+ Unlike `meta.edit.enabled`, its `row` is `Row<TMDataGridFeatures, TMDataGridRowData>`
162
+ with or without the column helper.
163
+
160
164
  The grid applies it in the editor host, around the field every editor writes
161
165
  through, so it covers the six built-ins, a custom `meta.edit.editor`, and the
162
166
  type-to-edit seed character.
@@ -241,7 +245,8 @@ unedited value and cannot pass. Use `"row"`.
241
245
  ## Cross-row rules
242
246
 
243
247
  `editing.tableValidators` holds the rules that need the other rows. Its
244
- `onSubmit` / `onSubmitAsync` receive `{ value, rowId, isNew, rows }`:
248
+ `onSubmit` / `onSubmitAsync` receive `TMDataGridTableValidateArgs`,
249
+ `{ value, rowId, isNew, rows }`:
245
250
  `value` is the committing row as drafted, and `rows` is
246
251
  `Array<{ rowId, value }>` - the collection as it would stand if the commit
247
252
  landed, with every draft overlaid, entry rows appended and deletion-marked
@@ -270,6 +275,56 @@ row's - after the row's own validators, and again for every committed row during
270
275
  `saveDrafts`, the only rules that run there: a committed row that a later edit
271
276
  invalidated is reopened with its errors, and the save reports it in `reopened`.
272
277
 
278
+ ## Derived columns and a table-wide rule
279
+
280
+ A column whose value depends on the other rows - a weight as a share of the
281
+ total - cannot be an `accessorFn`, which is handed one row. Derive the whole
282
+ collection with `useMemo` and pass the finished rows as `data`;
283
+ `editing.onCommit` writes back to the source array, and the derived rows arrive
284
+ on the next render. Under `mode: "cell"` with no draft store, every dependent
285
+ column follows the commit.
286
+
287
+ ```tsx
288
+ const positions = useMemo(() => {
289
+ const valued = holdings.map((h) => ({ ...h, marketValue: h.price * h.shares }));
290
+ const total = valued.reduce((sum, h) => sum + h.marketValue, 0);
291
+ return valued.map((h) => ({
292
+ ...h,
293
+ currentPct: (h.marketValue / total) * 100,
294
+ drift: h.targetPct - (h.marketValue / total) * 100,
295
+ }));
296
+ }, [holdings]);
297
+ ```
298
+
299
+ A rule over the whole collection, such as targets that may not total more than
300
+ 100%, is a `tableValidators` rule, while a bound on one cell (between 0 and 100)
301
+ stays on `meta.edit.validate`. `editing.columns` keeps every other column
302
+ read-only:
303
+
304
+ ```tsx
305
+ editing: {
306
+ mode: "cell",
307
+ // Only the target weight takes edits; everything else is market data.
308
+ columns: ["targetPct"],
309
+ onCommit: ({ rowId, value }) =>
310
+ setHoldings((previous) =>
311
+ previous.map((h) => (h.id === rowId ? { ...h, targetPct: value.targetPct } : h)),
312
+ ),
313
+ tableValidators: {
314
+ // `rows` already holds the committing row's drafted value.
315
+ onSubmit: ({ rows }) => {
316
+ const total = rows.reduce((sum, r) => sum + Number(r.value.targetPct ?? 0), 0);
317
+ return total > 100.005
318
+ ? { fields: { targetPct: `Targets would total ${pct(total)}` } }
319
+ : undefined;
320
+ },
321
+ },
322
+ }
323
+ ```
324
+
325
+ Source: `packages/tmdatagrid/docs/portfolio-rebalancer.md`, and the demo
326
+ `apps/docs/src/examples/demos/recipes/PortfolioRebalancer.tsx`.
327
+
273
328
  ## Server-side errors
274
329
 
275
330
  `editing.rowValidators.onSubmitAsync` returns TanStack Form's `{ form, fields }`
@@ -18,7 +18,7 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '2.0.0'
21
+ library_version: '2.1.0'
22
22
  sources:
23
23
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/filtering.md'
24
24
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/quick-search.md'
@@ -424,20 +424,26 @@ Source: `packages/tmdatagrid/docs/quick-search.md` (Fuzzy by default).
424
424
  | `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter opens on. |
425
425
  | `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. Module scope. |
426
426
  | `filterFn` | Column option | name or fn | `"tmDataGrid"` | Custom matching for one column. |
427
- | `quickSearchMode` | Option | `"fuzzy" \| "contains"` | `"fuzzy"` | How the quick search matches. |
427
+ | `quickSearchMode` | Option | `TMDataGridQuickSearchMode`: `"fuzzy" \| "contains"` | `"fuzzy"` | How the quick search matches. |
428
428
  | `enableMatchHighlighting` | Option | `boolean` | `false` | Mark matched text in default-rendered cells. |
429
429
  | `enableGlobalFilter` | Table option | `boolean` | `true` | Also a column option. Removes the input, or one column's participation. |
430
430
  | `globalFilterFn` | Table option | filter fn | fuzzy | Overrides the matching, and the ranking with it. |
431
431
  | `TMDataGrid.FilterPanel` | Component | `layout: "row" \| "stacked"`, Mantine `BoxProps` | `"row"` | The panel of filter rows, as a plain block. Style props set on it. |
432
+ | `TMDataGridFilterPanelProps` · `TMDataGridFilterPanelLayout` | Types | – · `"row" \| "stacked"` | – | The props of `TMDataGrid.FilterPanel`, and the type of its `layout`. For wrapping the panel in a component of your own. |
432
433
  | `filters` | Table option | `TMDataGridFiltersOptions` | `{ surface: "popup" }` | Which surface holds the filter controls. |
433
- | `TMDataGridFilterControlArgs.layout` | Type | `"row" \| "stacked" \| "header"` | – | How much room a value control has, and whether it names itself. |
434
- | `filterValueShape` | Export | `(operator) => "scalar" \| "set" \| "range"` | – | Which shape an operator's value takes. |
434
+ | `TMDataGridFiltersSettings` | Type | `Required<TMDataGridFiltersOptions>` | – | The `filters` option with its defaults filled in, as `api.filters`. |
435
+ | `TMDataGridFilterSurface` · `TMDataGridFilterSidebarSide` | Types | `"popup" \| "sidebar" \| "none"` · `"left" \| "right"` | – | The types of `filters.surface` and `filters.sidebarSide`. |
436
+ | `TMDataGridFilterControlArgs.layout` | Type | `TMDataGridFilterControlLayout` | – | How much room a value control has, and whether it names itself. |
437
+ | `TMDataGridFilterControlLayout` | Type | `"row" \| "stacked" \| "header"` | – | The type of `layout` on `TMDataGridFilterControlArgs`. `"header"` is the cell of the `inHeader` row. |
438
+ | `filterValueShape` | Export | `(operator) => TMDataGridFilterValueShape` | – | Which shape an operator's value takes. |
439
+ | `TMDataGridFilterValueShape` | Type | `"scalar" \| "set" \| "range"` | – | What `filterValueShape` returns. |
435
440
  | `TMDataGrid.FilterButton` | Component | – | – | Toolbar button opening the panel, with an active count. |
436
441
  | `TMDataGrid.FilterPills` | Component | `api`, `size`, `showClearAll`, `onPillClick`, Mantine `BoxProps` | – | Active filters as removable pills, renderable anywhere. Style props set on the wrapper. |
437
442
  | `TMDataGrid.Search` | Component | `placeholder`, `debounce` (`250`), `w` (`220`) | – | The debounced quick-search input. |
438
443
  | `openColumnFilter` | Export | `(api, columnId) => void` | – | Opens the panel on a column. |
439
444
  | `isFilterActive` | Export | `(value) => boolean` | – | Whether a filter value narrows anything. |
440
- | `activeColumnFilters` | Export | `(columnFilters \| table) => Array<{ id, value }>` | – | The filters in the grid's own value shape that narrow anything, typed. |
445
+ | `activeColumnFilters` | Export | `(columnFilters \| table) => Array<TMDataGridColumnFilter>` | – | The filters in the grid's own value shape that narrow anything, typed. |
446
+ | `TMDataGridColumnFilter` | Type | `{ id, value }` | – | One entry of `columnFilters`, with `value` typed as `TMDataGridFilterValue`. |
441
447
  | `getOperatorsForType` | Export | `(type) => operators` | – | The operator list a type offers. |
442
448
  | `getColumnOperators` · `getColumnDefaultOperator` | Exports | `(column) => operators` · `(column) => operator` | – | One column's list after `meta.filter.operators`, and the operator a fresh filter on it opens on. |
443
449
  | `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
@@ -5,16 +5,20 @@ description: >
5
5
  Mantine. Covers useTMDataGrid, the TMDataGrid root, context, the component
6
6
  catalog (Table, Footer, Toolbar, Spacer, SummaryCount, Search,
7
7
  LoadingIndicator, DraftActions, FilterButton, Menu, FilterPanel, FilterPills,
8
- ColumnsPanel), the size scale and the bounded-height layout requirement. Load
9
- when adding a grid, choosing which parts to render, or when rows do not
10
- appear.
8
+ ColumnsPanel) with their props types, the size scale, the bounded-height
9
+ layout requirement, and rendering rows without TMDataGrid.Table through
10
+ getDisplayedRows (a card view). Load when adding a grid, choosing which parts
11
+ to render, replacing the Table with a renderer of your own, or when rows do
12
+ not appear.
11
13
  metadata:
12
14
  type: core
13
15
  library: '@jielga/tmdatagrid'
14
- library_version: '2.0.0'
16
+ library_version: '2.1.0'
15
17
  sources:
16
18
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/getting-started.md'
17
19
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/anatomy.md'
20
+ - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/components.md'
21
+ - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/card-view.md'
18
22
  - 'Jielga/TMDataGrid:packages/tmdatagrid/src/components/TMDataGrid.tsx'
19
23
  - 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/sizes.ts'
20
24
  ---
@@ -131,6 +135,7 @@ TanStack ships state and APIs for both but no `enable` option.
131
135
  | Configure the hook, or persist state | `options` |
132
136
  | Theme, size, compose the toolbar, translate | `appearance` |
133
137
  | Write tests against a grid | `testing` |
138
+ | Upgrade code written against a 2.0 beta | `migrating-to-2` |
134
139
 
135
140
  ## Layout
136
141
 
@@ -156,7 +161,7 @@ flex column.
156
161
  | `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
157
162
  | `TMDataGrid.Spacer` | - | Pushes later toolbar items right. |
158
163
  | `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
159
- | `TMDataGrid.Search` | `placeholder`, `debounce` (default `250`), `w` (default `220`) | Quick search over every column, debounced into `globalFilter`. Renders nothing under `enableGlobalFilter: false`. |
164
+ | `TMDataGrid.Search` | `placeholder`, `debounce` (default `250`), `w` (default `220`) | Quick search over every column, debounced into `globalFilter`. Renders nothing under `enableGlobalFilter: false`. Also exported as `TMDataGridSearch`. |
160
165
  | `TMDataGrid.LoadingIndicator` | - | Small spinner while `meta.loading` is `true` and rows stay on screen. |
161
166
  | `TMDataGrid.DraftActions` | `renderActions` | Save with the pending count, and Discard. Renders nothing while editing is off - see the `editing` skill. |
162
167
  | `TMDataGrid.FilterButton` | - | Toggles the filter surface, seeding a filter row on the first filterable column. Renders nothing if no column is filterable, or under `filters.surface: "none"`. |
@@ -165,6 +170,29 @@ flex column.
165
170
  | `TMDataGrid.ColumnsPanel` | - | The column chooser as plain controls, for a Popover or a Drawer. |
166
171
  | `TMDataGrid.FilterPills` | `api`, `size` (default `"sm"`), `showClearAll` (default `true`), `onPillClick(columnId)`, `className` | One pill per active filter, ✕ to clear it. Takes the api as a prop, so it can be rendered outside the grid. Also exported as `TMDataGridFilterPills`. |
167
172
 
173
+ Each component's props type is exported, for wrapping a part in a component of
174
+ your own:
175
+
176
+ | Component | Props type |
177
+ | --- | --- |
178
+ | `TMDataGrid` | `TMDataGridProps<TData>`: the fields of `TMDataGridApi<TData>`, plus `children`, `size`, `className`, `style`, `id` and `data-testid` |
179
+ | `TMDataGrid.Table` | `TMDataGridTableProps<TData>` |
180
+ | `TMDataGrid.Toolbar` | `TMDataGridToolbarProps`: `children`, `withBottomBorder` (default `false`), Mantine `BoxProps` |
181
+ | `TMDataGrid.Search` · `TMDataGridSearch` | `TMDataGridSearchProps` |
182
+ | `TMDataGrid.Footer` | `TMDataGridFooterProps` |
183
+ | `TMDataGrid.Menu` | `TMDataGridMenuProps`: `children`, `icon`, `label`, Mantine `MenuProps` |
184
+ | `TMDataGrid.Menu.Columns` | `TMDataGridMenuColumnsProps`: `searchable` |
185
+ | `TMDataGrid.Menu.Export` · `.ExportSelected` | `TMDataGridMenuExportProps`: per-item `exportOptions` overrides, `columns` (which also takes `"custom"`), `label` |
186
+ | `TMDataGrid.ColumnsPanel` | `TMDataGridColumnsPanelProps`: `searchable`, Mantine `BoxProps` |
187
+ | `TMDataGrid.FilterPanel` | `TMDataGridFilterPanelProps`: `layout`, Mantine `BoxProps` |
188
+ | `TMDataGrid.FilterPills` · `TMDataGridFilterPills` | `TMDataGridFilterPillsProps<TData>` |
189
+ | `TMDataGrid.DraftActions` · `TMDataGridDraftActions` | `TMDataGridDraftActionsProps`: `renderActions` |
190
+
191
+ `renderActions` on `TMDataGrid.DraftActions` receives
192
+ `TMDataGridDraftActionsSlotArgs`, `{ state, actions, Controls }`, typed
193
+ `TMDataGridDraftActionsState`, `TMDataGridDraftActionsActions` and
194
+ `TMDataGridDraftActionsControls`. The fields are in the `editing` skill.
195
+
168
196
  Pass the row type so `onRowClick` stays typed:
169
197
 
170
198
  ```tsx
@@ -249,6 +277,76 @@ The virtualizer needs row height as a number, so it cannot come from CSS alone.
249
277
  `SIZE_ROW_HEIGHT` is the exported source of these values and the stylesheet
250
278
  mirrors them. Set `meta.rowHeight` for a height outside the scale.
251
279
 
280
+ ## Render rows without TMDataGrid.Table
281
+
282
+ To show the rows as something other than a table - cards, a list - keep
283
+ `useTMDataGrid` and `<TMDataGrid>`, and replace `TMDataGrid.Table` with a
284
+ renderer of your own. `TMDataGrid` renders no rows itself, and every other part
285
+ works without the Table, so the toolbar stays. Search, filters, sorting, column
286
+ visibility and row selection write the same table state the grid would.
287
+
288
+ ```tsx
289
+ const grid = useTMDataGrid({
290
+ data,
291
+ columns,
292
+ getRowId: (row) => String(row.id),
293
+ // The popup and the sidebar belong to TMDataGrid.Table.
294
+ filters: { surface: "none" },
295
+ });
296
+
297
+ <TMDataGrid {...grid} style={{ flex: 1, minHeight: 0 }}>
298
+ <TMDataGrid.Toolbar>
299
+ <TMDataGrid.Search />
300
+ <TMDataGrid.SummaryCount />
301
+ <TMDataGrid.Menu>
302
+ <TMDataGrid.Menu.Columns />
303
+ </TMDataGrid.Menu>
304
+ </TMDataGrid.Toolbar>
305
+ <TMDataGrid.FilterPanel layout="stacked" />
306
+ <CardList table={grid.table} features={grid.features} />
307
+ </TMDataGrid>;
308
+ ```
309
+
310
+ Read the rows with `getDisplayedRows(table, features)`: the rows the Table
311
+ would render, in render order - filtered, sorted, the current page when paging
312
+ is active, pinned rows left out. Call it inside a selector with a shallow
313
+ compare:
314
+
315
+ ```tsx
316
+ import { useSelector } from "@tanstack/react-store";
317
+ import { shallow } from "@tanstack/store";
318
+ import { getDisplayedRows } from "@jielga/tmdatagrid";
319
+
320
+ const rows = useSelector(table.store, () => getDisplayedRows(table, features), {
321
+ compare: shallow,
322
+ });
323
+ ```
324
+
325
+ The table identity never changes, so the React Compiler caches a bare
326
+ `getDisplayedRows(table, features)` call and the list stops following filters
327
+ and sorting. The shallow compare re-renders the list only when the rows change.
328
+ Read `row.getVisibleCells()` the same way, inside
329
+ `useSelector(table.store, () => row.getVisibleCells())`, and skip the generated
330
+ columns with `isGeneratedColumn(cell.column.id)` - the checkbox column is among
331
+ the visible cells while row selection is on. Render each value through the
332
+ column's own renderer: `flexRender(cell.column.columnDef.cell, cell.getContext())`.
333
+
334
+ The following belong to `TMDataGrid.Table` and are not available without it:
335
+
336
+ - the header, with click-to-sort, resizing, dragging and the column menus -
337
+ sort from a control of your own with `table.setSorting`
338
+ - the filter popup and sidebar - set `filters: { surface: "none" }` and place
339
+ `TMDataGrid.FilterPanel` yourself
340
+ - row details, row pinning, cell selection and editing in cells
341
+ - `scrollToRow`, which returns `false` while no Table is mounted
342
+
343
+ Virtualize the list yourself, for example with `useVirtualizer` from
344
+ `@tanstack/react-virtual`.
345
+
346
+ Source: `packages/tmdatagrid/docs/card-view.md`,
347
+ `packages/tmdatagrid/docs/anatomy.md` (Which rows it renders), and the demo
348
+ `apps/docs/src/examples/demos/recipes/CardView.tsx`.
349
+
252
350
  ## Helpers
253
351
 
254
352
  | Export | Description |
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0'
17
+ library_version: '2.1.0'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/grouping.md'
20
20
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/summary-row.md'
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: migrating-to-2
3
+ description: >
4
+ Upgrade code written against a 2.0.0-beta release of TMDataGrid to 2.0.0, as a
5
+ checklist to run over a codebase. Covers the edit.commitAll / saveDrafts /
6
+ addRows result objects and the silent `if (await saveDrafts())` trap, the
7
+ onSaveDrafts return type renamed to TMDataGridSaveDraftsResponse, every
8
+ removed name with its replacement (submitAll, onCommitDrafts, rows / added,
9
+ pendingCount, cellExport, exportGridToCsv, the cell-matrix functions,
10
+ toExcelCsv, downloadTextFile, labels.exportCsv), the helpers no longer
11
+ exported, removing row annotations and casts from meta.options and
12
+ meta.edit.enabled callbacks, and the behaviour changes. Load when upgrading
13
+ @jielga/tmdatagrid from a 2.0 beta, or when an import or a property from the
14
+ beta no longer resolves.
15
+ metadata:
16
+ type: lifecycle
17
+ library: '@jielga/tmdatagrid'
18
+ library_version: '2.1.0'
19
+ sources:
20
+ - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/migrating-to-2.md'
21
+ - 'Jielga/TMDataGrid:packages/tmdatagrid/src/index.ts'
22
+ ---
23
+
24
+ # TMDataGrid - Migrating from the 2.0 beta to 2.0.0
25
+
26
+ 2.0.0 removes every name deprecated during the 2.0 beta, stops exporting a set
27
+ of internal helpers, and changes what three edit calls resolve. Run the steps
28
+ below in order over the codebase. Step 1 finds code that still compiles but
29
+ behaves differently; the compiler finds most of the rest.
30
+
31
+ ## 1. CRITICAL Read `ok` from commitAll and saveDrafts
32
+
33
+ `edit.commitAll()` and `edit.saveDrafts()` resolve an object instead of a
34
+ `boolean`. A truthiness check on the old `boolean` still compiles and is now
35
+ always true, so a failed save reports success with no error.
36
+
37
+ Search for: `commitAll(` and `saveDrafts(`, including the `actions.save` and
38
+ `actions.commitAll` of a `TMDataGrid.DraftActions` `renderActions` slot, which
39
+ resolve the same objects.
40
+
41
+ | Call | Resolves |
42
+ | --- | --- |
43
+ | `edit.commitAll()` · `actions.commitAll()` | `TMDataGridCommitAllResult`: `{ ok, committed, open }` |
44
+ | `edit.saveDrafts()` · `actions.save()` | `TMDataGridSaveDraftsResult`: `{ ok, saved, kept, reopened }` |
45
+ | `edit.addRows(rows, options)` | `TMDataGridAddRowsResult`: `{ ok, committed, open }` - `ok` is new |
46
+ | `edit.commit(rowId)` | `boolean`, unchanged |
47
+
48
+ Wrong (compiles, always true):
49
+
50
+ ```tsx
51
+ if (await grid.edit.saveDrafts()) notify("Saved");
52
+ const done = await grid.edit.commitAll();
53
+ if (!done) return;
54
+ ```
55
+
56
+ Correct:
57
+
58
+ ```tsx
59
+ const { ok, kept, reopened } = await grid.edit.saveDrafts();
60
+ if (ok) notify("Saved");
61
+ else notify(`${kept.length + reopened.length} rows need attention`);
62
+
63
+ const { ok: committed } = await grid.edit.commitAll();
64
+ if (!committed) return;
65
+ ```
66
+
67
+ Each list holds row ids, and temp ids for new rows. `ok` is `true` when `open`
68
+ is empty, or, for `saveDrafts()`, when `kept` and `reopened` are both empty.
69
+ Also check every other use of the result: `.then((saved) => ...)`, a `return`
70
+ of it from a `boolean` function, `!result`, `result ? ... : ...` and
71
+ `Boolean(result)`.
72
+
73
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Read the batch edit results).
74
+
75
+ ## 2. Rename the onSaveDrafts return type
76
+
77
+ The type of what `editing.onSaveDrafts` may return is renamed
78
+ `TMDataGridSaveDraftsResponse`. `TMDataGridSaveDraftsResult` is now the type
79
+ `edit.saveDrafts()` resolves, so code that kept the old name for the callback
80
+ fails to compile.
81
+
82
+ Search for: `TMDataGridSaveDraftsResult`. Where it types an `onSaveDrafts`
83
+ callback or its return value, replace it:
84
+
85
+ ```tsx
86
+ // beta
87
+ import type { TMDataGridSaveDraftsResult } from "@jielga/tmdatagrid";
88
+
89
+ // 2.0.0
90
+ import type { TMDataGridSaveDraftsResponse } from "@jielga/tmdatagrid";
91
+ ```
92
+
93
+ Where it types the value of `await edit.saveDrafts()`, keep it.
94
+
95
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Rename the onSaveDrafts return type).
96
+
97
+ ## 3. Replace removed names
98
+
99
+ Search for each name in the first column and replace it:
100
+
101
+ | Removed | Use instead |
102
+ | --- | --- |
103
+ | `edit.submitAll()` | `edit.commitAll()`, then `edit.saveDrafts()` |
104
+ | `editing.onCommitDrafts` | `editing.onSaveDrafts` |
105
+ | `rows` and `added` in the `onSaveDrafts` payload | `updated` and `created` |
106
+ | `TMDataGridEditCommitDraftsArgs` | `TMDataGridSaveDraftsArgs` |
107
+ | `state.pendingCount` in a `TMDataGrid.DraftActions` slot | `state.draftCount` or `state.openCount` |
108
+ | `cellExport` on `TMDataGrid.Table` | `exportOptions` on `useTMDataGrid` |
109
+ | `exportGridToCsv` | `exportGrid` |
110
+ | `TMDataGridCellExportOptions`, `DEFAULT_CELL_EXPORT_OPTIONS`, `fromCellExportOptions` | `TMDataGridExportOptions`, `DEFAULT_EXPORT_OPTIONS` |
111
+ | `buildCellMatrix`, `buildGridCellMatrix`, `BuildCellMatrixArgs`, `TMDataGridCellMatrix` | `buildExportData`, `BuildExportDataArgs`, `TMDataGridExportData` |
112
+ | `toExcelCsv` | `csvExcelFormat` |
113
+ | `downloadTextFile` | `downloadFile` |
114
+ | `labels.exportCsv` | `labels.exportCells` |
115
+
116
+ Search for `onSaveDrafts` as well, and check what each callback reads off its
117
+ payload.
118
+
119
+ ### submitAll
120
+
121
+ `submitAll()` was `commitAll()` followed by `saveDrafts()`. Call both, and
122
+ combine the results where one answer is needed:
123
+
124
+ ```tsx
125
+ const committed = await grid.edit.commitAll();
126
+ const saved = await grid.edit.saveDrafts();
127
+ const ok = committed.ok && saved.ok;
128
+ ```
129
+
130
+ ### The CSV export options
131
+
132
+ `separator` and `decimalComma` move into a format; `includeHeaders` and
133
+ `fileName` stay in the options. `exportOptions` on the hook applies to every
134
+ export the grid offers, the cell-range menu included.
135
+
136
+ ```tsx
137
+ // beta
138
+ <TMDataGrid.Table cellExport={{ separator: ",", decimalComma: false, fileName: "orders" }} />;
139
+ exportGridToCsv({ table, options: { separator: ",", decimalComma: false } });
140
+
141
+ // 2.0.0
142
+ const exportOptions = {
143
+ format: csvExcelFormat({ separator: ",", decimalComma: false }),
144
+ fileName: "orders",
145
+ } satisfies TMDataGridExportOptions;
146
+
147
+ const grid = useTMDataGrid({ data, columns, exportOptions });
148
+ await exportGrid({ table: grid.table, options: exportOptions });
149
+ ```
150
+
151
+ ### The matrix functions
152
+
153
+ The export functions now return values, not text. A format writes the text:
154
+
155
+ ```tsx
156
+ // beta
157
+ const csv = toExcelCsv(buildGridCellMatrix({ table }), { separator: ";" });
158
+ downloadTextFile({ fileName: "export.csv", text: csv });
159
+
160
+ // 2.0.0
161
+ const data = buildExportData({ table });
162
+ const csv = await csvExcelFormat().write(data, { includeHeaders: true });
163
+ downloadFile({ fileName: "export.csv", content: csv, mimeType: "text/csv;charset=utf-8" });
164
+ ```
165
+
166
+ For a cell range, pass `rows` and `bounds` to `buildExportData` instead of
167
+ `rows`, `columns` and `bounds` to `buildCellMatrix`. `toClipboardText(data)`
168
+ writes the tab-separated text for the clipboard.
169
+
170
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Replace removed names).
171
+
172
+ ## 4. Remove annotations and casts from meta callbacks
173
+
174
+ The `row` that `meta.options` and `meta.edit.enabled` callbacks receive is
175
+ typed with the row type of `createTMDataGridColumnHelper<TData>()`:
176
+ `Row<TMDataGridFeatures, TData>`. A callback whose parameter is annotated with
177
+ the untyped row no longer compiles.
178
+
179
+ Search for: `options:` and `enabled:` inside column `meta`, and
180
+ `Row<TMDataGridFeatures, TMDataGridRowData>` or `as ` casts on `row.original`
181
+ next to them. Remove the annotation and the cast:
182
+
183
+ ```tsx
184
+ // beta
185
+ meta: {
186
+ edit: {
187
+ enabled: (row: Row<TMDataGridFeatures, TMDataGridRowData>) =>
188
+ (row.original as Employee).status !== "Terminated",
189
+ },
190
+ }
191
+
192
+ // 2.0.0
193
+ meta: {
194
+ edit: { enabled: (row) => row.original.status !== "Terminated" },
195
+ }
196
+ ```
197
+
198
+ Columns built without the helper keep the untyped row,
199
+ `Row<TMDataGridFeatures, TMDataGridRowData>`; leave those as they are.
200
+
201
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Remove annotations from meta callbacks).
202
+
203
+ ## 5. Replace helpers that are no longer exported
204
+
205
+ These names are internal to the grid and are no longer exported from
206
+ `@jielga/tmdatagrid`. An import of one fails to compile. Search the imports
207
+ from `@jielga/tmdatagrid` for each:
208
+
209
+ | Removed | Use instead |
210
+ | --- | --- |
211
+ | `getDefaultOperator` | `getColumnDefaultOperator(column)` |
212
+ | `isColumnEditableForRow` | `edit.canEditCell(row, column)` |
213
+ | `isColumnReorderable` | `getColumnCapabilities(column, features).canReorder` |
214
+ | `measureColumnContentWidth` | `autosizeColumn` |
215
+ | `tmDataGridFeatures` | The `TMDataGridFeatures` type |
216
+ | `isSameCell`, `resolveCellMove`, `ResolveCellMoveArgs`, `TMDataGridCellCoords`, `TMDataGridCellNav` | No public replacement; internal to the grid. |
217
+ | `boundsCellCount`, `boundsEdges`, `isWithinBounds` | No public replacement; internal to the grid. |
218
+ | `getColumnFilterControl` | No public replacement; internal to the grid. |
219
+ | `TMDataGridColumnLayout` | No public replacement; internal to the grid. |
220
+
221
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Replace un-exported helpers).
222
+
223
+ ## 6. Check the behaviour changes
224
+
225
+ - Under `editing.draft` without `onSaveDrafts`, a deletion whose
226
+ `onRowDelete` throws keeps its deletion mark and is reported in `kept`. In
227
+ the beta, `saveDrafts()` rejected and the mark was lost. Search for a
228
+ `try` / `catch` around `saveDrafts()` that expected the rejection, and read
229
+ `kept` instead.
230
+ - The Swedish labels `TMDATAGRID_LABELS_SV` say "Välj" for selecting: "Välj
231
+ alla", "Välj alla rader", "Välj rad" and "Välj grupp". Update tests that find
232
+ these controls by their Swedish name.
233
+
234
+ Source: `packages/tmdatagrid/docs/migrating-to-2.md` (Behaviour changes).
235
+
236
+ ## 7. Verify
237
+
238
+ Run the project's typecheck: an import of a removed or un-exported name, and a
239
+ meta callback annotated with the untyped row, fail there. Then run the tests
240
+ that save or commit edits, since the truthiness check of step 1 and the
241
+ behaviour changes of step 6 compile.
242
+
243
+ See also: the `editing` skill for the result objects and the draft store, and
244
+ the `data` skill for the export API.
@@ -14,9 +14,10 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0'
17
+ library_version: '2.1.0'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/use-tm-data-grid.md'
20
+ - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/persistence.md'
20
21
  - 'Jielga/TMDataGrid:packages/tmdatagrid/src/useTMDataGrid.tsx'
21
22
  - 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/persistence.ts'
22
23
  ---
@@ -30,6 +31,8 @@ const grid = useTMDataGrid<TData>(options);
30
31
  // { table, ui, edit, features, labels, resetSettings, scrollToRow }
31
32
  ```
32
33
 
34
+ The signature is
35
+ `useTMDataGrid<TData>(options: UseTMDataGridOptions<TData>): TMDataGridApi<TData>`.
33
36
  Spread the result onto `TMDataGrid`.
34
37
 
35
38
  ## Options
@@ -124,9 +127,9 @@ changes. Separate keys let one group be cleared without touching the other.
124
127
 
125
128
  | Field | Type | Default | Description |
126
129
  | --- | --- | --- | --- |
127
- | `dataKey` | `string \| [string, DataSlice[]]` | – | Storage key for the data group. |
128
- | `settingsKey` | `string \| [string, SettingsSlice[]]` | – | Storage key for the settings group. |
129
- | `storageMode` | `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. |
130
+ | `dataKey` | `TMDataGridPersistKey<TMDataGridDataSlice>`: `string \| [string, DataSlice[]]` | – | Storage key for the data group. |
131
+ | `settingsKey` | `TMDataGridPersistKey<TMDataGridSettingsSlice>`: `string \| [string, SettingsSlice[]]` | – | Storage key for the settings group. |
132
+ | `storageMode` | `TMDataGridStorageMode`: `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. `"sessionStorage"` is per tab. |
130
133
  | `serialize` | `(value) => string` | `JSON.stringify` | Serializes before storing. |
131
134
  | `deserialize` | `(value: string) => unknown` | `JSON.parse` | Parses a stored payload. |
132
135
 
@@ -168,7 +171,8 @@ survives the data changing under it the way the column layout does. `expanded` i
168
171
  a data slice for the opposite reason.
169
172
 
170
173
  `DATA_STATE_SLICES` and `SETTINGS_STATE_SLICES` export the same values. Slice
171
- names are typed per group, so only valid names are accepted.
174
+ names are typed per group, `TMDataGridDataSlice` and `TMDataGridSettingsSlice`,
175
+ so only valid names are accepted.
172
176
 
173
177
  Restoring happens once on mount through `initialState`. Writing is a subscription
174
178
  to the table store, so state changed directly through the table API is persisted
@@ -176,12 +180,20 @@ too. Only selected slices are read back, and unrecognised keys are ignored. All
176
180
  storage access is guarded - if storage is unavailable, disabled or full,
177
181
  persistence is skipped rather than throwing.
178
182
 
183
+ A payload from another version is dropped whole, not migrated. Payloads carry
184
+ the exported `PERSIST_PAYLOAD_VERSION`; anything else, including everything
185
+ written by a 0.x build, is discarded. Restored state is realigned against the
186
+ columns that exist: entries naming a column removed between deploys are
187
+ dropped.
188
+
189
+ Source: `packages/tmdatagrid/docs/persistence.md` (Behaviour).
190
+
179
191
  ## Return value
180
192
 
181
193
  | Field | Type | Description |
182
194
  | --- | --- | --- |
183
195
  | `table` | `Table<TMDataGridFeatures, TData>` | The TanStack table instance. |
184
- | `ui` | `Store<TMDataGridUiState, TMDataGridUiActions>` | State of the filter and column panels. |
196
+ | `ui` | `TMDataGridUiStore`: `Store<TMDataGridUiState, TMDataGridUiActions>` | State of the filter and column panels. |
185
197
  | `edit` | `TMDataGridEditApi` | The edit engine, inert until `editing` is set. See the `editing` skill. |
186
198
  | `features` | `TMDataGridFeatureFlags` | Table-level feature switches, re-read on each render. |
187
199
  | `labels` | `TMDataGridLabels` | The resolved label set, overrides merged over English. |
@@ -9,15 +9,15 @@ description: >
9
9
  onCellDoubleClick / onCellContextMenu, the renderRowContextMenu slot with its
10
10
  internalItems handback, renderColumnMenuItems and rowContextMenuProps, per-row
11
11
  styling with rowStyle, rowClassName, striped and the --row-bg rule, the row
12
- details panel through renderDetails and DETAILS_COLUMN_ID, row pinning with
13
- enableRowPinning and row.pin, and the row-number gutter through
14
- enableRowNumbers. Load when selecting rows, reacting to a click, opening a
15
- detail panel, colouring rows by their data, pinning rows to an edge, or
16
- numbering them.
12
+ details panel through renderDetails, detailsColumnPosition and
13
+ DETAILS_COLUMN_ID, row pinning with enableRowPinning and row.pin, and the
14
+ row-number gutter through enableRowNumbers. Load when selecting rows, reacting
15
+ to a click, opening a detail panel, colouring rows by their data, pinning rows
16
+ to an edge, or numbering them.
17
17
  metadata:
18
18
  type: core
19
19
  library: '@jielga/tmdatagrid'
20
- library_version: '2.0.0'
20
+ library_version: '2.1.0'
21
21
  sources:
22
22
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-selection.md'
23
23
  - 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-interaction.md'
@@ -235,8 +235,9 @@ const grid = useTMDataGrid({
235
235
  Each panel is measured, so heights need not be uniform.
236
236
  `renderDetailsEstHeight` (default `160`) is what the virtualizer assumes for one
237
237
  it has not measured yet. A generated chevron column, `DETAILS_COLUMN_ID`, is
238
- prepended and pinned left after the checkbox and tree lanes; it cannot be
239
- hidden, moved, resized or unpinned, and its header opens and closes every panel.
238
+ pinned left after the checkbox and tree lanes, or right, inside the edit lane,
239
+ under `detailsColumnPosition: "right"`; it cannot be hidden, moved, resized or
240
+ unpinned, and its header opens and closes every panel.
240
241
 
241
242
  Which rows are open is TanStack's own `expanded` state, so anything can open
242
243
  one - `row.toggleExpanded()` from a menu item, `initialState.expanded`,