@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.
Files changed (157) 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 +269 -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 +49 -48
  47. package/skills/columns/SKILL.md +125 -70
  48. package/skills/columns/references/columns-api.md +59 -0
  49. package/skills/data/SKILL.md +112 -18
  50. package/skills/editing/SKILL.md +76 -42
  51. package/skills/editing/references/common-mistakes.md +77 -69
  52. package/skills/editing/references/editing-api.md +25 -20
  53. package/skills/editing/references/editors-and-validation.md +80 -18
  54. package/skills/filtering/SKILL.md +155 -41
  55. package/skills/getting-started/SKILL.md +116 -16
  56. package/skills/grouping/SKILL.md +31 -16
  57. package/skills/migrating-to-2/SKILL.md +244 -0
  58. package/skills/options/SKILL.md +24 -12
  59. package/skills/rows/SKILL.md +22 -18
  60. package/skills/rows/references/rows-api.md +10 -6
  61. package/skills/server-side/SKILL.md +170 -17
  62. package/skills/testing/SKILL.md +150 -32
  63. package/skills/testing-components/SKILL.md +230 -0
  64. package/skills/testing-editing/SKILL.md +240 -0
  65. package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
  66. package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
  67. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
  68. package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
  69. package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +5 -51
  70. package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
  71. package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
  72. package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
  73. package/src/components/TMDataGridExportPicker.module.css +77 -0
  74. package/src/components/TMDataGridExportPicker.tsx +234 -0
  75. package/src/components/TMDataGridFilterPanel.module.css +54 -0
  76. package/src/components/TMDataGridFilterPanel.tsx +348 -0
  77. package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
  78. package/src/components/TMDataGridFilterSurface.module.css +54 -0
  79. package/src/components/TMDataGridFilterSurface.tsx +167 -0
  80. package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
  81. package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
  82. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
  83. package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
  84. package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
  85. package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
  86. package/src/components/TMDataGridMenu.tsx +357 -0
  87. package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
  88. package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
  89. package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
  90. package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
  91. package/src/components/TMDataGridToolbar.tsx +181 -0
  92. package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
  93. package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
  94. package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
  95. package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
  96. package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
  97. package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
  98. package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
  99. package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
  100. package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
  101. package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
  102. package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
  103. package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
  104. package/src/components/filters/controlLayout.ts +32 -0
  105. package/src/components/filters/filterControlFor.ts +65 -0
  106. package/src/components/generatedColumns.tsx +187 -0
  107. package/src/{tmdatagrid/components → components}/icons.ts +1 -0
  108. package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
  109. package/src/components/useHideableColumns.ts +52 -0
  110. package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
  111. package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
  112. package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
  113. package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
  114. package/src/core/controlledStateSync.ts +108 -0
  115. package/src/core/deletedRows.ts +34 -0
  116. package/src/core/dom.ts +74 -0
  117. package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
  118. package/src/core/export.ts +704 -0
  119. package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
  120. package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
  121. package/src/core/filterSurface.ts +99 -0
  122. package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
  123. package/src/{tmdatagrid/core → core}/labels.ts +51 -6
  124. package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
  125. package/src/core/pageReset.ts +120 -0
  126. package/src/core/pagination.ts +81 -0
  127. package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
  128. package/src/{tmdatagrid/core → core}/summary.ts +20 -4
  129. package/src/{tmdatagrid/index.ts → index.ts} +69 -35
  130. package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
  131. package/src/useTMDataGridExport.ts +78 -0
  132. package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
  133. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
  134. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
  135. package/src/tmdatagrid/core/cellExport.ts +0 -320
  136. /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
  137. /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
  138. /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
  139. /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
  140. /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
  141. /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
  142. /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
  143. /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
  144. /package/src/{tmdatagrid/core → core}/capabilities.ts +0 -0
  145. /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
  146. /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
  147. /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
  148. /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
  149. /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
  150. /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
  151. /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
  152. /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
  153. /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
  154. /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
  155. /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
  156. /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
  157. /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
@@ -1,320 +0,0 @@
1
- import type { Column, Row, RowData } from "@tanstack/react-table";
2
- import type { TMDataGridRowData } from "../TMDataGridContext";
3
- import type { TMDataGridFeatures, TMDataGridTable } from "../useTMDataGrid";
4
- import type { TMDataGridRangeBounds } from "./cellRange";
5
- import { getColumnLabel, isControlColumn } from "./columnUtils";
6
-
7
- /**
8
- * The byte order mark Excel looks for before it will read a file as UTF-8.
9
- * Built from its code point rather than pasted in, because a literal BOM is
10
- * invisible in a source file and easily deleted by accident.
11
- */
12
- const UTF8_BOM = String.fromCharCode(0xfeff);
13
-
14
- /** The selected block, flattened to text. One inner array per row. */
15
- export type TMDataGridCellMatrix = Array<Array<string>>;
16
-
17
- /**
18
- * How values are written out.
19
- *
20
- * The defaults are the Nordic ones, because they are the ones that need
21
- * choosing: an Excel running a Swedish, Norwegian, Danish or Finnish locale
22
- * reads `;` as its list separator and `,` as its decimal mark, and a file
23
- * written the other way opens as one column of text. Both are settable for a
24
- * grid whose users run something else.
25
- */
26
- export type TMDataGridCellExportOptions = {
27
- /** CSV field separator. Defaults to `";"`. */
28
- separator?: string;
29
- /** Write numbers as `1,5` rather than `1.5`. Defaults to `true`. */
30
- decimalComma?: boolean;
31
- /** Column labels as the first row. Defaults to `true`. */
32
- includeHeaders?: boolean;
33
- /** Without extension. Defaults to `"export"`. */
34
- fileName?: string;
35
- };
36
-
37
- type ResolvedExportOptions = Required<TMDataGridCellExportOptions>;
38
-
39
- export const DEFAULT_CELL_EXPORT_OPTIONS: ResolvedExportOptions = {
40
- separator: ";",
41
- decimalComma: true,
42
- includeHeaders: true,
43
- fileName: "export",
44
- };
45
-
46
- /**
47
- * One value as text.
48
- *
49
- * Deliberately not the rendered cell: what a cell renders is React, and often a
50
- * badge, a link or an icon rather than the value. The value is what a
51
- * spreadsheet wants, and it is the one thing every column is guaranteed to
52
- * have.
53
- */
54
- export function formatExportValue(
55
- value: unknown,
56
- { decimalComma }: { decimalComma: boolean },
57
- ): string {
58
- if (value === null || value === undefined) return "";
59
- if (typeof value === "number") {
60
- if (!Number.isFinite(value)) return "";
61
- const text = String(value);
62
- return decimalComma ? text.replace(".", ",") : text;
63
- }
64
- if (typeof value === "string") return value;
65
- if (typeof value === "boolean") return value ? "true" : "false";
66
- // A multiSelect cell holds an array of values; a spreadsheet cell holds one
67
- // string, so the elements are joined the way they read on screen.
68
- if (Array.isArray(value)) {
69
- return value
70
- .map((entry) => formatExportValue(entry, { decimalComma }))
71
- .join(", ");
72
- }
73
- // `sv-SE` is ISO-shaped (2026-07-31, 2026-07-31 14:05:00), which is both the
74
- // Nordic form and the one Excel parses as a date rather than as text.
75
- if (value instanceof Date) {
76
- return Number.isNaN(value.getTime()) ? "" : value.toLocaleString("sv-SE");
77
- }
78
- // An object in a cell is a shape the grid cannot know. JSON at least keeps
79
- // what was there, where `String(value)` would write "[object Object]".
80
- if (typeof value === "object") {
81
- try {
82
- return JSON.stringify(value);
83
- } catch {
84
- return "";
85
- }
86
- }
87
- return String(value);
88
- }
89
-
90
- export type BuildCellMatrixArgs = {
91
- rows: ReadonlyArray<Row<TMDataGridFeatures, TMDataGridRowData>>;
92
- /** Every visible column, in render order - the same list the bounds index into. */
93
- columns: ReadonlyArray<Column<TMDataGridFeatures, TMDataGridRowData, unknown>>;
94
- bounds: TMDataGridRangeBounds;
95
- includeHeaders: boolean;
96
- decimalComma: boolean;
97
- };
98
-
99
- /**
100
- * The selected rectangle as rows of text.
101
- *
102
- * The generated lanes - the checkbox, the tree chevron, the details chevron -
103
- * are dropped even when the rectangle covers them. They hold controls rather
104
- * than data, so a column of empty strings is all they could contribute, and
105
- * pasting one into a spreadsheet only shifts everything to its right.
106
- */
107
- export function buildCellMatrix({
108
- rows,
109
- columns,
110
- bounds,
111
- includeHeaders,
112
- decimalComma,
113
- }: BuildCellMatrixArgs): TMDataGridCellMatrix {
114
- const selectedColumns = columns
115
- .slice(bounds.left, bounds.right + 1)
116
- .filter((column) => !isControlColumn(column.id));
117
- if (selectedColumns.length === 0) return [];
118
-
119
- const matrix: TMDataGridCellMatrix = [];
120
- if (includeHeaders) {
121
- matrix.push(selectedColumns.map((column) => getColumnLabel(column)));
122
- }
123
-
124
- for (let index = bounds.top; index <= bounds.bottom; index += 1) {
125
- const row = rows[index];
126
- if (row === undefined) continue;
127
- matrix.push(
128
- selectedColumns.map((column) =>
129
- formatExportValue(row.getValue(column.id), { decimalComma }),
130
- ),
131
- );
132
- }
133
- return matrix;
134
- }
135
-
136
- /**
137
- * The whole grid as rows of text: every filtered and sorted data row - all
138
- * pages, not the one on screen - by every visible non-control column, in
139
- * render order.
140
- *
141
- * The values are the same ones a cell-range export writes; only the bounds
142
- * differ. Group rows are left out (their records are the rows themselves),
143
- * and hidden columns are not exported - what you see is what you get, minus
144
- * paging.
145
- */
146
- export function buildGridCellMatrix<TData extends RowData>({
147
- table,
148
- includeHeaders = DEFAULT_CELL_EXPORT_OPTIONS.includeHeaders,
149
- decimalComma = DEFAULT_CELL_EXPORT_OPTIONS.decimalComma,
150
- }: {
151
- table: TMDataGridTable<TData>;
152
- includeHeaders?: boolean;
153
- decimalComma?: boolean;
154
- }): TMDataGridCellMatrix {
155
- // The same erasure the context provider performs: the matrix only ever reads
156
- // generic row and column APIs.
157
- const erased = table as unknown as TMDataGridTable<TMDataGridRowData>;
158
- const columns = [
159
- ...erased.getLeftVisibleLeafColumns(),
160
- ...erased.getCenterVisibleLeafColumns(),
161
- ...erased.getRightVisibleLeafColumns(),
162
- ];
163
- // The sorted model: after filtering, grouping and sorting, before expansion
164
- // and paging - so a paged grid exports the whole filtered set, and a grouped
165
- // grid exports the records under every group whether or not it is open.
166
- //
167
- // Walked by hand rather than taken from `flatRows`: in table-core
168
- // 9.0.0-beta.21 the grouped model lists every leaf twice there (once as a
169
- // leaf row, once re-parented under its group), and its order puts the
170
- // groups last. Depth-first over `rows` is exactly render order, once each.
171
- const rows: Array<Row<TMDataGridFeatures, TMDataGridRowData>> = [];
172
- const walk = (
173
- list: ReadonlyArray<Row<TMDataGridFeatures, TMDataGridRowData>>,
174
- ) => {
175
- for (const row of list) {
176
- if (row.getIsGrouped()) walk(row.subRows);
177
- else rows.push(row);
178
- }
179
- };
180
- walk(erased.getSortedRowModel().rows);
181
-
182
- return buildCellMatrix({
183
- rows,
184
- columns,
185
- bounds: {
186
- top: 0,
187
- bottom: rows.length - 1,
188
- left: 0,
189
- right: columns.length - 1,
190
- },
191
- includeHeaders,
192
- decimalComma,
193
- });
194
- }
195
-
196
- /**
197
- * Downloads the whole grid as a CSV for Excel - {@link buildGridCellMatrix}
198
- * through {@link toExcelCsv}, with the same Nordic defaults and overrides as
199
- * the cell-range export.
200
- *
201
- * No built-in button: wire it to your own toolbar.
202
- *
203
- * ```tsx
204
- * <Button onClick={() => exportGridToCsv({ table: grid.table })}>Export</Button>
205
- * ```
206
- */
207
- export function exportGridToCsv<TData extends RowData>({
208
- table,
209
- options,
210
- }: {
211
- table: TMDataGridTable<TData>;
212
- options?: TMDataGridCellExportOptions;
213
- }): void {
214
- const resolved = { ...DEFAULT_CELL_EXPORT_OPTIONS, ...options };
215
- const matrix = buildGridCellMatrix({
216
- table,
217
- includeHeaders: resolved.includeHeaders,
218
- decimalComma: resolved.decimalComma,
219
- });
220
- if (matrix.length === 0) return;
221
- downloadTextFile({
222
- fileName: `${resolved.fileName}.csv`,
223
- text: toExcelCsv(matrix, { separator: resolved.separator }),
224
- });
225
- }
226
-
227
- /**
228
- * Quotes a field when it holds something that would otherwise end it early.
229
- * Doubling the quote is how both CSV and Excel's own clipboard format escape
230
- * one, so the same rule serves both.
231
- */
232
- function escapeField(value: string, separator: string): string {
233
- const needsQuotes =
234
- value.includes(separator) ||
235
- value.includes('"') ||
236
- value.includes("\n") ||
237
- value.includes("\r");
238
- return needsQuotes ? `"${value.replaceAll('"', '""')}"` : value;
239
- }
240
-
241
- /**
242
- * The clipboard format spreadsheets read: tab between cells, CRLF between rows.
243
- *
244
- * Tabs rather than commas because that is what Excel, Sheets and Numbers all
245
- * put on the clipboard themselves - paste it and the cells land in cells. A
246
- * comma-separated string pastes into a single column, which is the thing this
247
- * exists to avoid.
248
- */
249
- export function toClipboardText(matrix: TMDataGridCellMatrix): string {
250
- return matrix
251
- .map((row) => row.map((value) => escapeField(value, "\t")).join("\t"))
252
- .join("\r\n");
253
- }
254
-
255
- /**
256
- * A CSV that opens straight into columns in Excel.
257
- *
258
- * Three things make that true, and all three are needed:
259
- *
260
- * | Part | Why |
261
- * | ---- | --- |
262
- * | `sep=;` first line | Excel's own directive - it stops guessing and uses this |
263
- * | UTF-8 BOM | without it Excel reads the file as ANSI, and å ä ö arrive broken |
264
- * | CRLF line endings | what Excel writes, and what its importer is happiest with |
265
- *
266
- * The `sep=` line is Excel's alone; other readers show it as a first row. This
267
- * export targets Excel, so opening correctly there takes priority over strict
268
- * CSV.
269
- */
270
- export function toExcelCsv(
271
- matrix: TMDataGridCellMatrix,
272
- { separator }: { separator: string },
273
- ): string {
274
- const body = matrix
275
- .map((row) => row.map((value) => escapeField(value, separator)).join(separator))
276
- .join("\r\n");
277
- return `${UTF8_BOM}sep=${separator}\r\n${body}\r\n`;
278
- }
279
-
280
- /**
281
- * Puts text on the clipboard, reporting whether it landed.
282
- *
283
- * The async clipboard API only resolves for a document that has the focus and a
284
- * user gesture behind it - both true when this runs off Ctrl+C or a menu item.
285
- * It is still allowed to reject (a permissions policy, a page that lost focus
286
- * mid-copy), so the result is a boolean the caller can act on.
287
- */
288
- export async function writeClipboardText(text: string): Promise<boolean> {
289
- try {
290
- await navigator.clipboard.writeText(text);
291
- return true;
292
- } catch {
293
- return false;
294
- }
295
- }
296
-
297
- /**
298
- * Downloads text as a file, through the one mechanism a library can use: an
299
- * anchor with an object URL behind it, clicked. Revoked on the next frame -
300
- * immediately would race the browser's own read of it.
301
- */
302
- export function downloadTextFile({
303
- fileName,
304
- text,
305
- mimeType = "text/csv;charset=utf-8",
306
- }: {
307
- fileName: string;
308
- text: string;
309
- mimeType?: string;
310
- }): void {
311
- const url = URL.createObjectURL(new Blob([text], { type: mimeType }));
312
- const anchor = document.createElement("a");
313
- anchor.href = url;
314
- anchor.download = fileName;
315
- anchor.style.display = "none";
316
- document.body.append(anchor);
317
- anchor.click();
318
- anchor.remove();
319
- setTimeout(() => URL.revokeObjectURL(url), 0);
320
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes