@jielga/tmdatagrid 2.0.0-beta.9 → 2.0.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.
Files changed (154) hide show
  1. package/README.md +5 -212
  2. package/dist/index.d.ts +1281 -768
  3. package/dist/index.js +4607 -3250
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/docs/adding-rows.md +132 -0
  7. package/docs/anatomy.md +119 -0
  8. package/docs/card-view.md +108 -0
  9. package/docs/cell-selection.md +194 -0
  10. package/docs/column-layout.md +182 -0
  11. package/docs/column-menu.md +66 -0
  12. package/docs/columns.md +268 -0
  13. package/docs/components.md +311 -0
  14. package/docs/draft-store.md +242 -0
  15. package/docs/editing.md +303 -0
  16. package/docs/editors.md +250 -0
  17. package/docs/export.md +319 -0
  18. package/docs/filtering.md +362 -0
  19. package/docs/getting-started.md +123 -0
  20. package/docs/grouping.md +165 -0
  21. package/docs/loading-and-empty.md +92 -0
  22. package/docs/localization.md +79 -0
  23. package/docs/menu.md +143 -0
  24. package/docs/migrating-to-2.md +163 -0
  25. package/docs/pagination.md +144 -0
  26. package/docs/persistence.md +114 -0
  27. package/docs/portfolio-rebalancer.md +94 -0
  28. package/docs/query-builder.md +179 -0
  29. package/docs/quick-search.md +84 -0
  30. package/docs/row-details.md +115 -0
  31. package/docs/row-interaction.md +149 -0
  32. package/docs/row-pinning.md +132 -0
  33. package/docs/row-selection.md +136 -0
  34. package/docs/row-styling.md +133 -0
  35. package/docs/scrolling.md +112 -0
  36. package/docs/server-query.md +246 -0
  37. package/docs/server-side.md +206 -0
  38. package/docs/sorting.md +101 -0
  39. package/docs/styling.md +126 -0
  40. package/docs/summary-row.md +76 -0
  41. package/docs/testing.md +744 -0
  42. package/docs/toolbar.md +161 -0
  43. package/docs/use-tm-data-grid.md +361 -0
  44. package/package.json +22 -46
  45. package/skills/appearance/SKILL.md +72 -19
  46. package/skills/cell-selection/SKILL.md +46 -47
  47. package/skills/columns/SKILL.md +90 -34
  48. package/skills/data/SKILL.md +86 -16
  49. package/skills/editing/SKILL.md +67 -40
  50. package/skills/editing/references/common-mistakes.md +77 -69
  51. package/skills/editing/references/editing-api.md +22 -19
  52. package/skills/editing/references/editors-and-validation.md +24 -17
  53. package/skills/filtering/SKILL.md +148 -40
  54. package/skills/getting-started/SKILL.md +17 -15
  55. package/skills/grouping/SKILL.md +31 -16
  56. package/skills/options/SKILL.md +7 -7
  57. package/skills/rows/SKILL.md +22 -18
  58. package/skills/server-side/SKILL.md +170 -17
  59. package/skills/testing/SKILL.md +150 -32
  60. package/skills/testing-components/SKILL.md +230 -0
  61. package/skills/testing-editing/SKILL.md +240 -0
  62. package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
  63. package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
  64. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
  65. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
  66. package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +5 -51
  67. package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
  68. package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
  69. package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
  70. package/src/components/TMDataGridExportPicker.module.css +77 -0
  71. package/src/components/TMDataGridExportPicker.tsx +234 -0
  72. package/src/components/TMDataGridFilterPanel.module.css +54 -0
  73. package/src/components/TMDataGridFilterPanel.tsx +348 -0
  74. package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
  75. package/src/components/TMDataGridFilterSurface.module.css +54 -0
  76. package/src/components/TMDataGridFilterSurface.tsx +167 -0
  77. package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
  78. package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
  79. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
  80. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
  81. package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
  82. package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
  83. package/src/components/TMDataGridMenu.tsx +357 -0
  84. package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
  85. package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
  86. package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
  87. package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
  88. package/src/components/TMDataGridToolbar.tsx +181 -0
  89. package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
  90. package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
  91. package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
  92. package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
  93. package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
  94. package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
  95. package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
  96. package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
  97. package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
  98. package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
  99. package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
  100. package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
  101. package/src/components/filters/controlLayout.ts +32 -0
  102. package/src/components/filters/filterControlFor.ts +65 -0
  103. package/src/components/generatedColumns.tsx +187 -0
  104. package/src/{tmdatagrid/components → components}/icons.ts +1 -0
  105. package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
  106. package/src/components/useHideableColumns.ts +52 -0
  107. package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
  108. package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
  109. package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
  110. package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
  111. package/src/core/controlledStateSync.ts +108 -0
  112. package/src/core/deletedRows.ts +34 -0
  113. package/src/core/dom.ts +74 -0
  114. package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
  115. package/src/core/export.ts +704 -0
  116. package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
  117. package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
  118. package/src/core/filterSurface.ts +99 -0
  119. package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
  120. package/src/{tmdatagrid/core → core}/labels.ts +51 -6
  121. package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
  122. package/src/core/pageReset.ts +120 -0
  123. package/src/core/pagination.ts +81 -0
  124. package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
  125. package/src/{tmdatagrid/core → core}/summary.ts +20 -4
  126. package/src/{tmdatagrid/index.ts → index.ts} +69 -35
  127. package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
  128. package/src/useTMDataGridExport.ts +78 -0
  129. package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
  130. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
  131. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
  132. package/src/tmdatagrid/core/cellExport.ts +0 -320
  133. /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
  134. /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
  135. /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
  136. /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
  137. /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
  138. /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
  139. /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
  140. /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
  141. /package/src/{tmdatagrid/core → core}/capabilities.ts +0 -0
  142. /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
  143. /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
  144. /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
  145. /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
  146. /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
  147. /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
  148. /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
  149. /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
  150. /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
  151. /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
  152. /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
  153. /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
  154. /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
@@ -0,0 +1,92 @@
1
+ # Loading and empty states
2
+
3
+ A grid can have nothing to show for four different reasons, and the body says
4
+ which one it is: still fetching, emptied by a filter, or holding no data.
5
+
6
+ ```demo
7
+ file: data/LoadingAndEmpty.tsx
8
+ hint: Switch to loaded, then search for something that cannot match, to see the other branch.
9
+ extraSources: data/employeeColumns.tsx
10
+ ```
11
+
12
+ ## Precedence
13
+
14
+ An empty body shows exactly one thing, decided in this order:
15
+
16
+ 1. **Loading** - `meta.loading` is true: a centred loader. A grid that is
17
+ fetching never reports itself as empty.
18
+ 2. **Entry rows** - an open entry row from `edit.addRow()`: the entry block
19
+ only, with no message beside the form.
20
+ 3. **`renderEmptyState`** - your node, centred where the message would be.
21
+ 4. **Filtered-empty** - a filter or search is active: a search icon and
22
+ `labels.noResults` ("No rows match your filters").
23
+ 5. **Truly-empty** - no data at all: `labels.noRows` ("No rows to show").
24
+
25
+ ## Replacing the message
26
+
27
+ `renderEmptyState` replaces states 4 and 5 with one render prop.
28
+ `hasActiveFilters` says which of the two it is replacing, so one prop can render
29
+ two different messages:
30
+
31
+ ```tsx
32
+ <TMDataGrid.Table<Employee>
33
+ renderEmptyState={({ hasActiveFilters, table }) =>
34
+ hasActiveFilters ? (
35
+ <Stack align="center" gap="xs">
36
+ <Text c="dimmed">Nothing matches your filters</Text>
37
+ <Button variant="light" onClick={() => table.resetColumnFilters()}>
38
+ Clear filters
39
+ </Button>
40
+ </Stack>
41
+ ) : (
42
+ <Stack align="center" gap="xs">
43
+ <Text c="dimmed">No employees yet</Text>
44
+ <Button onClick={openCreateModal}>Add the first one</Button>
45
+ </Stack>
46
+ )
47
+ }
48
+ />
49
+ ```
50
+
51
+ ## Loading with rows on screen
52
+
53
+ The body's loading state only appears while the grid is **empty**. A
54
+ server-driven grid refetching with rows still on screen keeps showing them
55
+ rather than blanking the body on every page change.
56
+
57
+ `TMDataGrid.LoadingIndicator` covers that case: a small spinner while
58
+ `meta.loading` is true, and nothing otherwise. Place it where you want it,
59
+ typically after `Spacer`.
60
+
61
+ ```tsx
62
+ <TMDataGrid.Toolbar>
63
+ <TMDataGrid.SummaryCount />
64
+ <TMDataGrid.Spacer />
65
+ <TMDataGrid.LoadingIndicator />
66
+ </TMDataGrid.Toolbar>
67
+ ```
68
+
69
+ ## Counting what is there
70
+
71
+ `TMDataGrid.SummaryCount` shows visible rows out of the total. The total is
72
+ `meta.totalRowCount` when you provide it, and the pre-filtered row count
73
+ otherwise. A [server-side](/docs/server-side) grid must provide it, because the
74
+ client only holds one page.
75
+
76
+ ```tsx
77
+ <TMDataGrid.SummaryCount>
78
+ {visible} of {total} employees
79
+ </TMDataGrid.SummaryCount>
80
+ ```
81
+
82
+ ## Reference
83
+
84
+ | Name | Kind | Type | Default | What it does |
85
+ | --- | --- | --- | --- | --- |
86
+ | `meta.loading` | Option | `boolean` | `false` | A fetch is in flight. Takes precedence over every empty message. |
87
+ | `meta.noResultsLabel` | Option | `string` | `labels.noResults` | The filtered-empty message, without a render prop. |
88
+ | `meta.totalRowCount` | Option | `number` | Pre-filtered count | The total `SummaryCount` reports. |
89
+ | `renderEmptyState` | Table prop | `({ hasActiveFilters, table }) => ReactNode` | – | Replaces both built-in empty messages. |
90
+ | `TMDataGrid.LoadingIndicator` | Component | – | – | Spinner while `meta.loading`, for when the body has rows. |
91
+ | `TMDataGrid.SummaryCount` | Component | `children` replaces the text | – | Visible rows out of total. |
92
+ | `labels.noRows` · `labels.noResults` | Labels | `string` | English defaults | The two built-in messages. See [Localization](/docs/localization). |
@@ -0,0 +1,79 @@
1
+ # Localization
2
+
3
+ Every string the grid renders - menu items, panels, tooltips, the pager and
4
+ every `aria-label` - comes from one labels object. English by default.
5
+
6
+ ```tsx
7
+ const grid = useTMDataGrid({
8
+ data,
9
+ columns,
10
+ labels: { noResults: "Inga rader matchar dina filter" },
11
+ });
12
+ ```
13
+
14
+ `labels` takes **any subset** and merges it over the defaults.
15
+
16
+ ```demo
17
+ file: customization/Localization.tsx
18
+ extraSources: data/employeeColumns.tsx
19
+ ```
20
+
21
+ ## A complete translation
22
+
23
+ A full Swedish dictionary ships as `TMDATAGRID_LABELS_SV`:
24
+
25
+ ```tsx
26
+ import { TMDATAGRID_LABELS_SV } from "@jielga/tmdatagrid";
27
+
28
+ const grid = useTMDataGrid({ data, columns, labels: TMDATAGRID_LABELS_SV });
29
+ ```
30
+
31
+ `TMDATAGRID_LABELS_EN` is the English base, and `TMDataGridLabels` is the full
32
+ dictionary type, so a missing key in a new translation is a compile error.
33
+
34
+ ## Labels that carry a value
35
+
36
+ These labels are functions, so each language can place the value where its
37
+ grammar requires:
38
+
39
+ ```tsx
40
+ labels: {
41
+ groupBy: (column) => `Gruppera på ${column}`,
42
+ pageRange: ({ from, to, total }) => `${from}–${to} av ${total}`,
43
+ }
44
+ ```
45
+
46
+ ## Keep the object stable
47
+
48
+ Define it at module scope, or memoize it. The grid re-renders when the labels
49
+ object changes identity.
50
+
51
+ ```tsx
52
+ const labels = { noResults: "Inga träffar" } satisfies TMDataGridLabelsOverride;
53
+ ```
54
+
55
+ ## Reading them yourself
56
+
57
+ The resolved dictionary comes back from the hook as `grid.labels`, and from
58
+ context as `useTMDataGridContext().labels`, so a
59
+ [toolbar component of your own](/docs/toolbar) uses the same strings as the
60
+ built-in parts, in whatever language is configured.
61
+
62
+ `mergeLabels(base, override)` is the merge itself, exported for composing
63
+ dictionaries before passing one in.
64
+
65
+ `meta.noResultsLabel` remains as a per-instance override of
66
+ `labels.noResults`.
67
+
68
+ ## Reference
69
+
70
+ | Name | Kind | Type | Default | What it does |
71
+ | --- | --- | --- | --- | --- |
72
+ | `labels` | Option | `TMDataGridLabelsOverride` | English | Any subset, merged over the defaults. Keep it stable. |
73
+ | `grid.labels` | Hook return | `TMDataGridLabels` | – | The resolved dictionary. |
74
+ | `TMDATAGRID_LABELS_EN` | Export | `TMDataGridLabels` | – | The English base. |
75
+ | `TMDATAGRID_LABELS_SV` | Export | `TMDataGridLabels` | – | A complete Swedish dictionary. |
76
+ | `TMDataGridLabels` | Export | type | – | The full dictionary. What a new translation must cover. |
77
+ | `TMDataGridLabelsOverride` | Export | type | – | A partial dictionary. |
78
+ | `mergeLabels` | Export | `(base, override) => TMDataGridLabels` | – | The merge, for composing dictionaries. |
79
+ | `meta.noResultsLabel` | Option | `string` | `labels.noResults` | Per-instance override of the filtered-empty message. |
package/docs/menu.md ADDED
@@ -0,0 +1,143 @@
1
+ # Grid menu
2
+
3
+ `TMDataGrid.Menu` is the burger button at the end of the toolbar and the Mantine `Menu` it opens.
4
+ Its children are the dropdown: Mantine `Menu.Item`s of your own, and the built-in items under `TMDataGrid.Menu.*`.
5
+
6
+ ```tsx
7
+ import { Menu } from "@mantine/core";
8
+
9
+ <TMDataGrid.Toolbar>
10
+ <TMDataGrid.SummaryCount />
11
+ <TMDataGrid.Spacer />
12
+ <TMDataGrid.FilterButton />
13
+ <TMDataGrid.Menu>
14
+ <TMDataGrid.Menu.Export />
15
+ <Menu.Item onClick={saveView}>Save view</Menu.Item>
16
+ <Menu.Divider />
17
+ <Menu.Label>Columns</Menu.Label>
18
+ <TMDataGrid.Menu.Columns />
19
+ </TMDataGrid.Menu>
20
+ </TMDataGrid.Toolbar>
21
+ ```
22
+
23
+ ```demo
24
+ file: customization/GridMenu.tsx
25
+ hint: The burger holds the app's own items above the column chooser.
26
+ ```
27
+
28
+ A custom item reads the grid from context, the same way a [toolbar button](/docs/toolbar#buttons-of-your-own) does.
29
+ `TMDataGrid.Menu.Export` and `TMDataGrid.Menu.ExportSelected` are the built-in export items; their props and formats are on [Export](/docs/export).
30
+ Mantine's `Menu.Divider`, `Menu.Label` and `Menu.Sub` work as they do in any Mantine menu; the grid wraps nothing of Mantine's.
31
+
32
+ `TMDataGrid.Menu` always renders: it cannot see whether its children render anything.
33
+ A menu holding only `TMDataGrid.Menu.Columns` on a grid with `enableHiding: false` opens empty, so hide it with the same check the built-in buttons use:
34
+
35
+ ```tsx
36
+ const { table, features } = useTMDataGridContext();
37
+ const { canHideAny } = getGridCapabilities(table, features);
38
+
39
+ {canHideAny && (
40
+ <TMDataGrid.Menu>
41
+ <TMDataGrid.Menu.Columns />
42
+ </TMDataGrid.Menu>
43
+ )}
44
+ ```
45
+
46
+ ## The column chooser as menu items
47
+
48
+ `TMDataGrid.Menu.Columns` is the whole chooser: one checkbox item per column that can be hidden, a search box once there are six of them, **Show/Hide All** and **Reset layout**.
49
+ It renders nothing when no column can be hidden.
50
+ The pieces it is made of are exported for menus that want only some of them.
51
+
52
+ | Component | Renders |
53
+ | --- | --- |
54
+ | `TMDataGrid.Menu.Columns` | `Menu.Search`, the toggles, a divider, show/hide all and reset layout. By default the search box shows from six columns; `searchable` shows it always and `searchable={false}` never. |
55
+ | `TMDataGrid.Menu.ColumnToggles` | One item per hideable column, with a checkbox that shows both states. `search` narrows the list to the labels containing it. |
56
+ | `TMDataGrid.Menu.ShowHideAll` | One checkbox item over the same list. An indeterminate box marks a partial state, and a click then shows all. |
57
+ | `TMDataGrid.Menu.ResetLayout` | One item calling `resetSettings()`: visibility, order, pinning and widths. |
58
+
59
+ Each of them needs a Mantine `Menu` around it and reads the grid from context, so they work in any Mantine menu rendered inside `TMDataGrid`, not only in the burger:
60
+
61
+ ```tsx
62
+ <Menu width={260} withinPortal>
63
+ <Menu.Target>
64
+ <Button size="compact-xs" variant="subtle">
65
+ View
66
+ </Button>
67
+ </Menu.Target>
68
+ <Menu.Dropdown>
69
+ <Menu.Item onClick={() => setDensity("compact")}>Compact rows</Menu.Item>
70
+ <Menu.Divider />
71
+ <TMDataGrid.Menu.ColumnToggles />
72
+ <Menu.Divider />
73
+ <TMDataGrid.Menu.ShowHideAll />
74
+ <TMDataGrid.Menu.ResetLayout />
75
+ </Menu.Dropdown>
76
+ </Menu>
77
+ ```
78
+
79
+ ### As a submenu
80
+
81
+ Every column header's menu has **Manage columns**, a `Menu.Sub` holding `TMDataGrid.Menu.Columns`.
82
+ The same composition works in a menu of your own:
83
+
84
+ ```tsx
85
+ <Menu.Sub>
86
+ <Menu.Sub.Target>
87
+ <Menu.Sub.Item>Manage columns</Menu.Sub.Item>
88
+ </Menu.Sub.Target>
89
+ <Menu.Sub.Dropdown>
90
+ <TMDataGrid.Menu.Columns searchable={false} />
91
+ </Menu.Sub.Dropdown>
92
+ </Menu.Sub>
93
+ ```
94
+
95
+ `searchable` is for a block at the top level of a dropdown.
96
+ `Menu.Search` registers on the root menu, which switches off type-ahead and the arrow keys of every dropdown of that menu, so a search box inside a submenu breaks the keyboard behaviour of the menu around it.
97
+ ArrowDown from the search box moves to the first listed column, whatever sits above the block.
98
+
99
+ ### The panel instead
100
+
101
+ `TMDataGrid.ColumnsPanel` is the same chooser as plain controls, for a host that is not a menu: a Popover, a Drawer, or a settings page.
102
+
103
+ ```tsx
104
+ const [opened, setOpened] = useState(false);
105
+
106
+ <Popover opened={opened} onChange={setOpened} withinPortal trapFocus>
107
+ <Popover.Target>
108
+ <ActionIcon aria-label="Columns" onClick={() => setOpened((open) => !open)}>
109
+ <IconColumns3 size={18} />
110
+ </ActionIcon>
111
+ </Popover.Target>
112
+ <Popover.Dropdown p={0}>
113
+ <TMDataGrid.ColumnsPanel />
114
+ </Popover.Dropdown>
115
+ </Popover>
116
+ ```
117
+
118
+ ## Labels
119
+
120
+ `labels.menuButton` is the burger's tooltip and `aria-label`.
121
+ The chooser uses the panel's strings: `columnsSearchPlaceholder`, `columnsNoMatch`, `columnsShowHideAll` and `columnsReset`.
122
+ See [Localization](/docs/localization).
123
+
124
+ ## Testing
125
+
126
+ The burger is `data-dg-part="menu-button"`.
127
+ The items publish the same parts as the panel: `columns-search`, `columns-toggle` with `data-column-id`, `columns-toggle-all` and `columns-reset`, so a test written against the panel reads the same on the menu.
128
+ The export items are `menu-export` and `menu-export-selected`.
129
+ See [Testing](/docs/testing).
130
+
131
+ ## Reference
132
+
133
+ | Name | Kind | Type | Default | What it does |
134
+ | --- | --- | --- | --- | --- |
135
+ | `TMDataGrid.Menu` | Component | Mantine `MenuProps` without `children`, plus `children`, `icon`, `label` | `position="bottom-end"`, `shadow="md"`, `width={260}`, `withinPortal` | The burger and its dropdown. `icon` replaces the burger; `label` is the tooltip and `aria-label`, default `labels.menuButton`. |
136
+ | `TMDataGrid.Menu.Columns` | Component | `searchable?: boolean \| "auto"` | `"auto"` | The whole column chooser as menu items. `"auto"` shows the search box from six columns. Renders nothing when no column can be hidden. |
137
+ | `TMDataGrid.Menu.ColumnToggles` | Component | `search?: string` | – | One checkbox item per hideable column. |
138
+ | `TMDataGrid.Menu.ShowHideAll` | Component | – | – | One checkbox item over every hideable column. |
139
+ | `TMDataGrid.Menu.ResetLayout` | Component | – | – | Calls `resetSettings()`. |
140
+ | `TMDataGrid.Menu.Export` · `.ExportSelected` | Components | `TMDataGridExportOptions` | – | The export items. Props on [Export](/docs/export). |
141
+ | `TMDataGrid.ColumnsPanel` | Component | – | – | The chooser as plain controls, for a host that is not a menu. |
142
+ | `labels.menuButton` | Option | `string` | `"Menu"` | The burger's tooltip and `aria-label`. |
143
+ | `TMDataGridMenuProps` · `TMDataGridMenuColumnsProps` · `TMDataGridMenuExportProps` | Export | types | – | The prop types. |
@@ -0,0 +1,163 @@
1
+ # Migrating from the 2.0 beta
2
+
3
+ 2.0.0 removes every name deprecated during the 2.0 beta, stops exporting a set of internal helpers, and changes what three edit calls resolve.
4
+ This page lists each change and the code that replaces it, from any `2.0.0-beta` release.
5
+
6
+ ## Read the batch edit results
7
+
8
+ `edit.commitAll()` and `edit.saveDrafts()` resolve an object instead of a `boolean`:
9
+
10
+ | Call | Resolves |
11
+ | --- | --- |
12
+ | `edit.commitAll()` | `{ ok, committed, open }` |
13
+ | `edit.saveDrafts()` | `{ ok, saved, kept, reopened }` |
14
+ | `edit.addRows(rows, options)` | `{ ok, committed, open }` - `ok` is new |
15
+
16
+ Important: a check on the old `boolean` still compiles and is now always true.
17
+ Search your code for `commitAll()` and `saveDrafts()` and read `ok`:
18
+
19
+ ```tsx
20
+ // beta
21
+ if (await grid.edit.saveDrafts()) notify("Saved");
22
+
23
+ // 2.0.0
24
+ const { ok, kept, reopened } = await grid.edit.saveDrafts();
25
+ if (ok) notify("Saved");
26
+ else notify(`${kept.length + reopened.length} rows need attention`);
27
+ ```
28
+
29
+ Each list holds row ids, and temp ids for new rows.
30
+ `ok` is `true` when `open` is empty, or, for `saveDrafts()`, when `kept` and `reopened` are both empty.
31
+ See [Saving the store](/docs/draft-store#saving-the-store) for what each list means.
32
+
33
+ The `actions.save` and `actions.commitAll` that `TMDataGrid.DraftActions` passes to a custom slot resolve the same objects.
34
+
35
+ `edit.commit(rowId)` still resolves a `boolean`.
36
+
37
+ ## Rename the onSaveDrafts return type
38
+
39
+ The type of what `editing.onSaveDrafts` may return is renamed `TMDataGridSaveDraftsResponse`.
40
+ `TMDataGridSaveDraftsResult` is now the type `edit.saveDrafts()` resolves, so code that kept the old name for the callback fails to compile:
41
+
42
+ ```tsx
43
+ // beta
44
+ import type { TMDataGridSaveDraftsResult } from "@jielga/tmdatagrid";
45
+
46
+ // 2.0.0
47
+ import type { TMDataGridSaveDraftsResponse } from "@jielga/tmdatagrid";
48
+ ```
49
+
50
+ ## Replace removed names
51
+
52
+ | Removed | Use instead |
53
+ | --- | --- |
54
+ | `edit.submitAll()` | `edit.commitAll()`, then `edit.saveDrafts()` |
55
+ | `editing.onCommitDrafts` | `editing.onSaveDrafts` |
56
+ | `rows` and `added` in the `onSaveDrafts` payload | `updated` and `created` |
57
+ | `TMDataGridEditCommitDraftsArgs` | `TMDataGridSaveDraftsArgs` |
58
+ | `state.pendingCount` in a `TMDataGrid.DraftActions` slot | `state.draftCount` or `state.openCount` |
59
+ | `cellExport` on `TMDataGrid.Table` | `exportOptions` on `useTMDataGrid` |
60
+ | `exportGridToCsv` | `exportGrid` |
61
+ | `TMDataGridCellExportOptions`, `DEFAULT_CELL_EXPORT_OPTIONS`, `fromCellExportOptions` | `TMDataGridExportOptions`, `DEFAULT_EXPORT_OPTIONS` |
62
+ | `buildCellMatrix`, `buildGridCellMatrix`, `BuildCellMatrixArgs`, `TMDataGridCellMatrix` | `buildExportData`, `BuildExportDataArgs`, `TMDataGridExportData` |
63
+ | `toExcelCsv` | `csvExcelFormat` |
64
+ | `downloadTextFile` | `downloadFile` |
65
+ | `labels.exportCsv` | `labels.exportCells` |
66
+
67
+ ### submitAll
68
+
69
+ `submitAll()` was `commitAll()` followed by `saveDrafts()`.
70
+ Call both, and combine the results where one answer is needed:
71
+
72
+ ```tsx
73
+ const committed = await grid.edit.commitAll();
74
+ const saved = await grid.edit.saveDrafts();
75
+ const ok = committed.ok && saved.ok;
76
+ ```
77
+
78
+ ### The CSV export options
79
+
80
+ `separator` and `decimalComma` move into a format; `includeHeaders` and `fileName` stay in the options:
81
+
82
+ ```tsx
83
+ // beta
84
+ <TMDataGrid.Table cellExport={{ separator: ",", decimalComma: false, fileName: "orders" }} />;
85
+ exportGridToCsv({ table, options: { separator: ",", decimalComma: false } });
86
+
87
+ // 2.0.0
88
+ const exportOptions = {
89
+ format: csvExcelFormat({ separator: ",", decimalComma: false }),
90
+ fileName: "orders",
91
+ } satisfies TMDataGridExportOptions;
92
+
93
+ const grid = useTMDataGrid({ data, columns, exportOptions });
94
+ await exportGrid({ table: grid.table, options: exportOptions });
95
+ ```
96
+
97
+ `exportOptions` on the hook applies to every export the grid offers, the cell-range menu included.
98
+
99
+ ### The matrix functions
100
+
101
+ The export functions now return values, not text.
102
+ A format writes the text:
103
+
104
+ ```tsx
105
+ // beta
106
+ const csv = toExcelCsv(buildGridCellMatrix({ table }), { separator: ";" });
107
+ downloadTextFile({ fileName: "export.csv", text: csv });
108
+
109
+ // 2.0.0
110
+ const data = buildExportData({ table });
111
+ const csv = await csvExcelFormat().write(data, { includeHeaders: true });
112
+ downloadFile({ fileName: "export.csv", content: csv, mimeType: "text/csv;charset=utf-8" });
113
+ ```
114
+
115
+ For a cell range, pass `rows` and `bounds` to `buildExportData` instead of `rows`, `columns` and `bounds` to `buildCellMatrix`.
116
+ `toClipboardText(data)` writes the tab-separated text for the clipboard.
117
+
118
+ ## Remove annotations from meta callbacks
119
+
120
+ The `row` that `meta.options` and `meta.edit.enabled` callbacks receive is typed with the row type of `createTMDataGridColumnHelper<TData>()`.
121
+ Remove casts on `row.original`.
122
+ A callback whose parameter is annotated with the untyped row no longer compiles; remove the annotation:
123
+
124
+ ```tsx
125
+ // beta
126
+ meta: {
127
+ edit: {
128
+ enabled: (row: Row<TMDataGridFeatures, TMDataGridRowData>) =>
129
+ (row.original as Employee).status !== "Terminated",
130
+ },
131
+ }
132
+
133
+ // 2.0.0
134
+ meta: {
135
+ edit: { enabled: (row) => row.original.status !== "Terminated" },
136
+ }
137
+ ```
138
+
139
+ Columns built without the helper keep the untyped row.
140
+
141
+ ## Replace un-exported helpers
142
+
143
+ These names are internal to the grid and are no longer exported from `@jielga/tmdatagrid`.
144
+ An import of one fails to compile:
145
+
146
+ | Removed | Use instead |
147
+ | --- | --- |
148
+ | `getDefaultOperator` | `getColumnDefaultOperator(column)` |
149
+ | `isColumnEditableForRow` | `edit.canEditCell(row, column)` |
150
+ | `isColumnReorderable` | `getColumnCapabilities(column, features).canReorder` |
151
+ | `measureColumnContentWidth` | `autosizeColumn` |
152
+ | `tmDataGridFeatures` | The `TMDataGridFeatures` type |
153
+ | `isSameCell`, `resolveCellMove`, `ResolveCellMoveArgs`, `TMDataGridCellCoords`, `TMDataGridCellNav` | No public replacement; internal to the grid. |
154
+ | `boundsCellCount`, `boundsEdges`, `isWithinBounds` | No public replacement; internal to the grid. |
155
+ | `getColumnFilterControl` | No public replacement; internal to the grid. |
156
+ | `TMDataGridColumnLayout` | No public replacement; internal to the grid. |
157
+
158
+ ## Behaviour changes
159
+
160
+ - Under `editing.draft` without `onSaveDrafts`, a deletion whose `onRowDelete` throws keeps its deletion mark and is reported in `kept`.
161
+ In the beta, `saveDrafts()` rejected and the mark was lost.
162
+ - The Swedish labels `TMDATAGRID_LABELS_SV` say "Välj" for selecting: "Välj alla", "Välj alla rader", "Välj rad" and "Välj grupp".
163
+ Update tests that find these controls by their Swedish name.
@@ -0,0 +1,144 @@
1
+ # Pagination
2
+
3
+ **Off by default.** The grid renders every filtered and sorted row and relies
4
+ on [virtualization](/docs/scrolling), which handles any row count. Turn paging
5
+ on when users should move through the data a page at a time, not to keep a
6
+ large grid responsive.
7
+
8
+ There are three modes.
9
+
10
+ **No pagination** - the default. `TMDataGrid.Footer` renders nothing.
11
+
12
+ ```tsx
13
+ const grid = useTMDataGrid({ data, columns });
14
+ ```
15
+
16
+ **Client pagination** - the table pages the data itself, and the Footer renders
17
+ its pager. Initial page size is 25, configurable through
18
+ `initialState.pagination`.
19
+
20
+ ```tsx
21
+ const grid = useTMDataGrid({ data, columns, enablePagination: true });
22
+ ```
23
+
24
+ **Manual pagination** - the server pages, and the grid stops.
25
+ `manualPagination: true` implies `enablePagination`, so no extra flag is
26
+ needed. See [Server-side data](/docs/server-side).
27
+
28
+ ```tsx
29
+ const grid = useTMDataGrid({
30
+ data: page.rows,
31
+ columns,
32
+ manualPagination: true,
33
+ rowCount: page.total,
34
+ state: { pagination },
35
+ onPaginationChange: setPagination,
36
+ });
37
+ ```
38
+
39
+ ```demo
40
+ file: data/Pagination.tsx
41
+ extraSources: data/employeeColumns.tsx
42
+ ```
43
+
44
+ `enablePagination` is defined by the grid rather than by TanStack, which ships
45
+ the pagination state and APIs but no `enable` option.
46
+
47
+ ## Replacing the pager
48
+
49
+ `TMDataGrid.Footer` takes a `renderPagination` slot, handed three things: the
50
+ `state` the pager is showing, the `actions` it can take, and `Controls` - the
51
+ built-in pieces, already wired.
52
+
53
+ ```tsx
54
+ <TMDataGrid.Footer
55
+ renderPagination={({ state, actions, Controls }) => (
56
+ <Group>
57
+ <Controls.PageSize />
58
+ <Button onClick={actions.previousPage} disabled={!state.canPreviousPage}>
59
+ Back
60
+ </Button>
61
+ <Text>
62
+ {state.pageIndex + 1} / {state.pageCount}
63
+ </Text>
64
+ <Button onClick={actions.nextPage} disabled={!state.canNextPage}>
65
+ Next
66
+ </Button>
67
+ </Group>
68
+ )}
69
+ />
70
+ ```
71
+
72
+ The default footer renders `PageSize`, `Range` and `Pager`, in that order. A
73
+ control kept from `Controls` behaves as it does in the default footer, including
74
+ greying out while a grouping suspends paging.
75
+
76
+ | Member | Renders |
77
+ | --- | --- |
78
+ | `Controls.PageSize` | "Rows per page" and its select |
79
+ | `Controls.Range` | The "1–25 of 300" label |
80
+ | `Controls.PageNumber` | The "Page 3 of 200" label |
81
+ | `Controls.Pager` | The previous and next buttons |
82
+
83
+ `PageNumber` is not in the default footer.
84
+ It is the label a server-paged grid usually shows in place of a row range, so it is put in through the slot:
85
+
86
+ ```tsx
87
+ <TMDataGrid.Footer
88
+ renderPagination={({ Controls }) => (
89
+ <>
90
+ <Controls.PageSize />
91
+ <Controls.PageNumber />
92
+ <Controls.Pager />
93
+ </>
94
+ )}
95
+ />
96
+ ```
97
+
98
+ It reads `pageCount`, so a grid declaring `pageCount: -1` shows "Page 3" alone.
99
+
100
+ `state` carries `pageIndex`, `pageSize`, `pageCount`, `rowCount`,
101
+ `canPreviousPage`, `canNextPage`, the `from` / `to` bounds of the current page,
102
+ and `isPagingActive`. `actions` carries `setPageIndex`, `setPageSize`,
103
+ `previousPage`, `nextPage`, `firstPage` and `lastPage`.
104
+
105
+ `getTMDataGridPaginationApi(table)` returns the same `{ state, actions }`
106
+ outside the Footer, for a pager that lives elsewhere on the page. `Controls` is
107
+ not included: those components are bound to the grid's context and work only
108
+ inside the slot.
109
+
110
+ ## Grouping
111
+
112
+ While a column is grouped the pager greys itself out and the range is replaced
113
+ with `Grouped · all N rows`. See
114
+ [Grouping](/docs/grouping#grouping-and-pagination).
115
+
116
+ A custom pager can grey itself out the same way:
117
+
118
+ ```tsx
119
+ <TMDataGrid.Footer
120
+ renderPagination={({ state, actions }) => (
121
+ <MyPager {...state} {...actions} disabled={!state.isPagingActive} />
122
+ )}
123
+ />
124
+ ```
125
+
126
+ `isPagingActive` is live state: whether the pager is currently slicing
127
+ anything. `getGridCapabilities(...).canPaginate` is configuration: whether
128
+ paging is switched on at all. The two differ while a grouping is active.
129
+
130
+ ## Reference
131
+
132
+ | Name | Kind | Type | Default | What it does |
133
+ | --- | --- | --- | --- | --- |
134
+ | `enablePagination` | Option | `boolean` | `false` | Client-side paging and the Footer's pager. Grid-defined. |
135
+ | `manualPagination` | Table option | `boolean` | `false` | The server pages. Implies `enablePagination`. |
136
+ | `rowCount` | Table option | `number` | – | The true total, required under `manualPagination`. |
137
+ | `initialState.pagination` | Table option | `{ pageIndex, pageSize }` | `{ 0, 25 }` | Where paging starts. A data slice, so it persists. |
138
+ | `onPaginationChange` | Table option | `OnChangeFn` | – | Controls the pagination state. |
139
+ | `resetPageOnQueryChange` | Option | `boolean` | `true` | Back to page 1 when a filter, the quick search, the sort or the grouping changes. `false` keeps the page. See [Server-side data](/docs/server-side#the-page-index). |
140
+ | `TMDataGrid.Footer` | Component | – | – | The footer bar. Renders nothing when paging is off. |
141
+ | `Footer` `renderPagination` | Slot | `({ state, actions, Controls }) => ReactNode` | Built-in pager | Replaces the pager, and hands over its pieces. |
142
+ | `getTMDataGridPaginationApi` | Export | `(table) => { state, actions }` | – | The pager API, outside the Footer. |
143
+ | `TMDataGridPaginationState` · `TMDataGridPaginationActions` · `TMDataGridPaginationControls` | Exports | types | – | The three parts of the slot's argument. |
144
+ | `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything right now. |