@jielga/tmdatagrid 1.0.0 → 1.0.2

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 (78) hide show
  1. package/README.md +8 -8
  2. package/dist/index.d.ts +225 -225
  3. package/dist/index.js +58 -50
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +3 -2
  7. package/skills/appearance/SKILL.md +322 -0
  8. package/skills/cell-selection/SKILL.md +240 -0
  9. package/skills/columns/SKILL.md +261 -86
  10. package/skills/data/SKILL.md +289 -0
  11. package/skills/editing/SKILL.md +492 -0
  12. package/skills/editing/references/editing-api.md +124 -0
  13. package/skills/editing/references/editors-and-validation.md +198 -0
  14. package/skills/filtering/SKILL.md +344 -0
  15. package/skills/getting-started/SKILL.md +48 -27
  16. package/skills/grouping/SKILL.md +264 -0
  17. package/skills/options/SKILL.md +31 -20
  18. package/skills/rows/SKILL.md +369 -0
  19. package/skills/rows/references/rows-api.md +117 -0
  20. package/skills/server-side/SKILL.md +7 -7
  21. package/skills/testing/SKILL.md +12 -12
  22. package/src/tmdatagrid/TMDataGridContext.ts +2 -2
  23. package/src/tmdatagrid/components/TMDataGrid.module.css +2 -2
  24. package/src/tmdatagrid/components/TMDataGrid.tsx +5 -5
  25. package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +4 -4
  26. package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +2 -2
  27. package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +6 -6
  28. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
  29. package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +4 -4
  30. package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +6 -6
  31. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +6 -6
  32. package/src/tmdatagrid/components/TMDataGridFilterPills.module.css +2 -2
  33. package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +1 -1
  34. package/src/tmdatagrid/components/TMDataGridFooter.module.css +1 -1
  35. package/src/tmdatagrid/components/TMDataGridFooter.tsx +12 -6
  36. package/src/tmdatagrid/components/TMDataGridGroupColumn.module.css +1 -1
  37. package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +5 -5
  38. package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +8 -8
  39. package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +12 -12
  40. package/src/tmdatagrid/components/TMDataGridRowNumberColumn.tsx +4 -4
  41. package/src/tmdatagrid/components/TMDataGridSearch.tsx +5 -5
  42. package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +8 -8
  43. package/src/tmdatagrid/components/TMDataGridTable.module.css +18 -18
  44. package/src/tmdatagrid/components/TMDataGridTable.tsx +116 -116
  45. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +5 -5
  46. package/src/tmdatagrid/components/editors/TMDataGridBooleanEditor.tsx +1 -1
  47. package/src/tmdatagrid/components/editors/TMDataGridDateEditor.tsx +2 -2
  48. package/src/tmdatagrid/components/editors/TMDataGridMultiSelectEditor.tsx +1 -1
  49. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
  50. package/src/tmdatagrid/components/editors/TMDataGridSelectEditor.tsx +2 -2
  51. package/src/tmdatagrid/components/editors/editorShared.ts +3 -3
  52. package/src/tmdatagrid/components/filters/DgDateRangeFilter.tsx +1 -1
  53. package/src/tmdatagrid/components/filters/DgRangeSliderFilter.tsx +1 -1
  54. package/src/tmdatagrid/components/filters/DgTriStateFilter.tsx +1 -1
  55. package/src/tmdatagrid/components/filters/TMDataGridFilterValueInput.tsx +2 -2
  56. package/src/tmdatagrid/components/sticky.module.css +8 -8
  57. package/src/tmdatagrid/core/autosize.ts +4 -4
  58. package/src/tmdatagrid/core/capabilities.ts +17 -17
  59. package/src/tmdatagrid/core/cellExport.ts +13 -13
  60. package/src/tmdatagrid/core/cellNavigation.ts +6 -6
  61. package/src/tmdatagrid/core/cellRange.ts +4 -4
  62. package/src/tmdatagrid/core/columnOptions.ts +2 -2
  63. package/src/tmdatagrid/core/columnOrdering.ts +2 -2
  64. package/src/tmdatagrid/core/columnUtils.ts +3 -3
  65. package/src/tmdatagrid/core/editEngine.ts +49 -49
  66. package/src/tmdatagrid/core/expanding.ts +5 -5
  67. package/src/tmdatagrid/core/filterControls.ts +5 -5
  68. package/src/tmdatagrid/core/filterOperators.ts +14 -14
  69. package/src/tmdatagrid/core/labels.ts +8 -8
  70. package/src/tmdatagrid/core/matchHighlight.ts +4 -4
  71. package/src/tmdatagrid/core/persistence.ts +8 -8
  72. package/src/tmdatagrid/core/quickSearch.ts +8 -8
  73. package/src/tmdatagrid/core/rowPinning.ts +3 -3
  74. package/src/tmdatagrid/core/rowSelection.ts +12 -12
  75. package/src/tmdatagrid/core/sizes.ts +1 -1
  76. package/src/tmdatagrid/core/summary.ts +3 -3
  77. package/src/tmdatagrid/useTMDataGrid.tsx +79 -79
  78. package/skills/features/SKILL.md +0 -352
@@ -0,0 +1,289 @@
1
+ ---
2
+ name: data
3
+ description: >
4
+ How a TMDataGrid presents the rows it has: pagination, virtualization, and
5
+ having nothing to show. Covers the three pagination modes (off,
6
+ enablePagination, manualPagination), the Footer's pagination render prop and
7
+ getTMDataGridPaginationApi, isPagingActive versus canPaginate, always-on
8
+ virtualization with overscan and meta.rowHeight, scrollToRow and scrollerRef,
9
+ the edge callbacks onScrollToBottom / onScrollToRight and why onReachEnd is
10
+ better for loading more, the header and pinned-lane depth shadows, and the
11
+ four empty states in precedence order with meta.loading, renderEmptyState,
12
+ hasActiveFilters, TMDataGrid.LoadingIndicator and TMDataGrid.SummaryCount.
13
+ Load when adding a pager, tuning scrolling, scrolling to a row, or deciding
14
+ what an empty grid should say.
15
+ metadata:
16
+ type: core
17
+ library: '@jielga/tmdatagrid'
18
+ library_version: '1.0.2'
19
+ sources:
20
+ - 'Jielga/TMDataGrid:src/docs/pagination.md'
21
+ - 'Jielga/TMDataGrid:src/docs/scrolling.md'
22
+ - 'Jielga/TMDataGrid:src/docs/loading-and-empty.md'
23
+ - 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGridFooter.tsx'
24
+ ---
25
+
26
+ # TMDataGrid - Pagination, scrolling and empty states
27
+
28
+ ## Pagination
29
+
30
+ **Off by default.** The grid renders every filtered and sorted row and relies on
31
+ virtualization, which handles any row count - so paging is a choice about how
32
+ the reader navigates, not a performance workaround. Three modes:
33
+
34
+ ```tsx
35
+ // 1. None (default). TMDataGrid.Footer renders nothing.
36
+ const grid = useTMDataGrid({ data, columns });
37
+
38
+ // 2. Client. The table pages, the Footer renders its pager.
39
+ const grid = useTMDataGrid({ data, columns, enablePagination: true });
40
+
41
+ // 3. Manual. The server pages; manualPagination implies enablePagination.
42
+ const grid = useTMDataGrid({
43
+ data: page.rows,
44
+ columns,
45
+ manualPagination: true,
46
+ rowCount: page.total,
47
+ state: { pagination },
48
+ onPaginationChange: setPagination,
49
+ });
50
+ ```
51
+
52
+ Initial page size is 25, through `initialState.pagination`. `enablePagination`
53
+ is one of the two switches the grid defines itself, and the one switch that
54
+ defaults to off.
55
+
56
+ `TMDataGrid.Footer` takes a `pagination` render prop handed the same API the
57
+ built-in pager is built on, and `getTMDataGridPaginationApi(table)` returns that
58
+ object outside the Footer:
59
+
60
+ ```tsx
61
+ <TMDataGrid.Footer
62
+ pagination={(api) => (
63
+ <Group>
64
+ <Button onClick={api.previousPage} disabled={!api.canPreviousPage}>
65
+ Back
66
+ </Button>
67
+ <Text>
68
+ {api.pageIndex + 1} / {api.pageCount}
69
+ </Text>
70
+ <Button onClick={api.nextPage} disabled={!api.canNextPage}>
71
+ Next
72
+ </Button>
73
+ </Group>
74
+ )}
75
+ />
76
+ ```
77
+
78
+ Grouping suspends the pager and wins: it greys out and the range becomes
79
+ `Grouped · all N rows`. `isPagingActive(table, features)` is the live state -
80
+ whether the pager is slicing anything right now - while
81
+ `getGridCapabilities(...).canPaginate` is the configuration. The two differ
82
+ exactly while a grouping is active.
83
+
84
+ ## Scrolling
85
+
86
+ Virtualization is **always on**. There is no flag, no threshold and no "enable
87
+ for large data sets": only the rows within the viewport plus a small overscan
88
+ are ever mounted.
89
+
90
+ `overscan` (default `6`) is the one knob - raise it if a fast scroll flashes
91
+ blank rows, lower it when rows are expensive. Row height comes from
92
+ `meta.rowHeight`, or from `size` when that is not set, and rows are **fixed
93
+ height**, so the virtualizer's estimate is exact and the scrollbar is honest.
94
+ Row details are the exception: a row showing a panel is measured after it
95
+ mounts.
96
+
97
+ ```tsx
98
+ const grid = useTMDataGrid({
99
+ data,
100
+ columns,
101
+ getRowId: (row) => String(row.id),
102
+ overscan: 12,
103
+ meta: { rowHeight: 64 },
104
+ });
105
+
106
+ grid.scrollToRow({ rowId: "42", align: "center" });
107
+ ```
108
+
109
+ `scrollToRow` exists because the row may not be mounted, which is exactly what
110
+ `element.scrollIntoView()` cannot handle. `align` is `"start"`, `"center"`,
111
+ `"end"` or `"auto"`. `scrollerRef` is the scroll container itself.
112
+
113
+ `TMDataGrid.Table` reports arrivals at each edge - `onScrollToTop`,
114
+ `onScrollToBottom`, `onScrollToLeft`, `onScrollToRight` - firing **once** on
115
+ arrival rather than on every scroll event. For loading more rows prefer
116
+ `onReachEnd` (the `server-side` skill): it fires rows early rather than at the
117
+ very bottom, and latches per row count so a pending fetch is never asked twice.
118
+
119
+ Two depth cues are scroll-driven animations on the compositor, with no scroll
120
+ listener and no React render: a shadow under the sticky header once rows scroll
121
+ beneath it, and a band beside a pinned lane while it is actually covering
122
+ something.
123
+
124
+ ## Empty states
125
+
126
+ There are four ways to have nothing to show, and an empty body shows exactly one
127
+ of them, in this order:
128
+
129
+ 1. **Loading** - `meta.loading` is true: a centred loader. A grid that is
130
+ fetching never claims to be empty.
131
+ 2. **Entry rows** - an open entry row from `edit.addRow()`: only the entry
132
+ block, with no message competing with the form.
133
+ 3. **`renderEmptyState`** - your node, centred where the message would be.
134
+ 4. **Filtered-empty** - a filter or search is active: `labels.noResults`,
135
+ because this emptiness is the reader's own doing.
136
+ 5. **Truly-empty** - no data at all: `labels.noRows`.
137
+
138
+ `renderEmptyState` replaces the last two with one render prop, and
139
+ `hasActiveFilters` says which it is standing in for:
140
+
141
+ ```tsx
142
+ <TMDataGrid.Table<Employee>
143
+ renderEmptyState={({ hasActiveFilters, table }) =>
144
+ hasActiveFilters ? (
145
+ <Button variant="light" onClick={() => table.resetColumnFilters()}>
146
+ Clear filters
147
+ </Button>
148
+ ) : (
149
+ <Button onClick={openCreateModal}>Add the first employee</Button>
150
+ )
151
+ }
152
+ />
153
+ ```
154
+
155
+ An empty grid is where a reader is most likely to be stuck, so it is worth
156
+ giving them the action that unsticks them.
157
+
158
+ The body's loading state only appears while the grid is **empty**. A
159
+ server-driven grid refetching with rows still on screen keeps showing them;
160
+ `TMDataGrid.LoadingIndicator` is the signal for that case, a small spinner while
161
+ `meta.loading` is true. `TMDataGrid.SummaryCount` shows visible rows out of
162
+ total, where the total is `meta.totalRowCount` when provided and the pre-filtered
163
+ count otherwise.
164
+
165
+ ## Common mistakes
166
+
167
+ ### CRITICAL Turning pagination on to make a large grid fast
168
+
169
+ Virtualization is already unconditional, so paging a 200 000-row grid changes
170
+ nothing about rendering cost - it only takes navigation away from the reader.
171
+ Reach for it when the reader should move page by page, not for performance.
172
+
173
+ Source: `src/docs/pagination.md`, `src/docs/scrolling.md`.
174
+
175
+ ### CRITICAL A variable row height
176
+
177
+ The virtualizer needs one number, and rows are fixed height so the scrollbar
178
+ stays honest. Styling a taller row through CSS leaves the measurement and the
179
+ render disagreeing: rows overlap or gaps open as you scroll, and the effect
180
+ depends on scroll position, so it looks intermittent.
181
+
182
+ Wrong:
183
+
184
+ ```css
185
+ [data-dg-part="row"] { height: 64px; }
186
+ ```
187
+
188
+ Correct:
189
+
190
+ ```tsx
191
+ useTMDataGrid({ data, columns, meta: { rowHeight: 64 } });
192
+ ```
193
+
194
+ Source: `src/docs/scrolling.md` (Row height).
195
+
196
+ ### HIGH `scrollIntoView` on a row that is not mounted
197
+
198
+ Only the viewport's rows exist in the DOM, so a query for row 4 000 returns
199
+ nothing and the call silently does nothing.
200
+
201
+ Correct:
202
+
203
+ ```tsx
204
+ grid.scrollToRow({ rowId: "4000", align: "center" });
205
+ ```
206
+
207
+ `scrollToRow` returns `false` when the row is not in the current view - filtered
208
+ out, on another page, or an id matching no row - and nothing scrolled.
209
+
210
+ Source: `src/docs/scrolling.md` (Scrolling to a row).
211
+
212
+ ### HIGH Loading more rows from `onScrollToBottom`
213
+
214
+ It fires at the very bottom, so the reader waits at the end of the list for the
215
+ fetch. `onReachEnd` fires rows early and latches per row count, so a pending
216
+ fetch is never asked twice.
217
+
218
+ Source: `src/docs/scrolling.md` (Edge callbacks).
219
+
220
+ ### HIGH `SummaryCount` reporting the page under manual pagination
221
+
222
+ The client only holds one page, so without `meta.totalRowCount` the total is
223
+ whatever arrived. It shows "25 of 25" over a table of thousands.
224
+
225
+ Correct:
226
+
227
+ ```tsx
228
+ useTMDataGrid({
229
+ data: page.rows,
230
+ columns,
231
+ manualPagination: true,
232
+ rowCount: page.total,
233
+ meta: { totalRowCount: page.total },
234
+ });
235
+ ```
236
+
237
+ Source: `src/docs/loading-and-empty.md` (Counting what is there).
238
+
239
+ ### MEDIUM Rendering an empty message while data is loading
240
+
241
+ The order exists so a fetching grid never claims to be empty. A hand-rolled
242
+ `data.length === 0 ? <Empty /> : <Grid />` outside the grid flashes "No rows to
243
+ show" on every load.
244
+
245
+ Correct:
246
+
247
+ ```tsx
248
+ useTMDataGrid({ data, columns, meta: { loading: isFetching } });
249
+ ```
250
+
251
+ Source: `src/docs/loading-and-empty.md` (What wins).
252
+
253
+ ### MEDIUM Trusting the pager while grouped
254
+
255
+ `getPageCount()` still returns a number, but the pager is greyed out and the
256
+ grid is rendering the whole tree. A custom pager must read
257
+ `isPagingActive(table, features)` rather than the page count.
258
+
259
+ Source: `src/docs/pagination.md` (Grouping suspends it).
260
+
261
+ ## Reference
262
+
263
+ | Name | Kind | Type | Default | What it does |
264
+ | --- | --- | --- | --- | --- |
265
+ | `enablePagination` | Option | `boolean` | `false` | Client-side paging and the Footer's pager. Grid-defined. |
266
+ | `manualPagination` | Table option | `boolean` | `false` | The server pages. Implies `enablePagination`. |
267
+ | `rowCount` | Table option | `number` | – | The true total, required under `manualPagination`. |
268
+ | `initialState.pagination` | Table option | `{ pageIndex, pageSize }` | `{ 0, 25 }` | Where paging starts. A `data` slice, so it persists. |
269
+ | `onPaginationChange` | Table option | `OnChangeFn` | – | Controls the pagination state. |
270
+ | `TMDataGrid.Footer` | Component | `pageSizeOptions`, `pagination` | `[10, 25, 50, 100]` | The footer bar. Renders nothing when paging is off. |
271
+ | `getTMDataGridPaginationApi` | Export | `(table) => TMDataGridPaginationApi` | – | The pager API, outside the Footer. |
272
+ | `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything right now. |
273
+ | `overscan` | Option | `number` | `6` | Rows kept mounted beyond each edge of the viewport. |
274
+ | `meta.rowHeight` | Option | `number` | From `size` | Row height in pixels. The virtualizer needs a number. |
275
+ | `scrollToRow` | Hook return | `({ rowId, align? }) => boolean` | `align: "auto"` | Scrolls a row into view, mounted or not. |
276
+ | `scrollerRef` | Hook return | `RefObject<HTMLDivElement>` | – | The scroll container. |
277
+ | `onScrollToTop` · `onScrollToBottom` · `onScrollToLeft` · `onScrollToRight` | Table props | `() => void` | – | Fire once on arriving at that edge. |
278
+ | `TMDataGridScrollAlign` | Export | `"start" \| "center" \| "end" \| "auto"` | – | The `align` argument. |
279
+ | `meta.loading` | Option | `boolean` | `false` | A fetch is in flight. Outranks every empty message. |
280
+ | `meta.noResultsLabel` | Option | `string` | `labels.noResults` | The filtered-empty message, without a render prop. |
281
+ | `meta.totalRowCount` | Option | `number` | Pre-filtered count | The total `SummaryCount` reports. |
282
+ | `renderEmptyState` | Table prop | `({ hasActiveFilters, table }) => ReactNode` | – | Replaces both built-in empty messages. |
283
+ | `TMDataGrid.LoadingIndicator` | Component | – | – | Spinner while `meta.loading`, for when the body has rows. |
284
+ | `TMDataGrid.SummaryCount` | Component | `children` replaces the text | – | Visible rows out of total. |
285
+ | `--dg-header-shadow-color` | CSS variable | colour | Themed | The shadow under the sticky header. |
286
+ | `--dg-sticky-edge-range` | CSS variable | length | `20px` | How far the pinned-lane band takes to fade in. |
287
+
288
+ See also: the `server-side` skill for `manualPagination` and `onReachEnd`, and
289
+ the `grouping` skill for why the pager suspends.