@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
@@ -15,15 +15,15 @@ import type { TMDataGridColumnType } from "./filterOperators";
15
15
  import type { TMDataGridSize } from "./sizes";
16
16
 
17
17
  /**
18
- * How commits happen — one axis, each mode a thin policy over the same
18
+ * How commits happen - one axis, each mode a thin policy over the same
19
19
  * engine. See `editMode` on `UseTMDataGridOptions`.
20
20
  */
21
21
  export type TMDataGridEditMode = "cell" | "cellConfirm" | "row" | "batch";
22
22
 
23
23
  /**
24
24
  * One editing row's live form. TanStack Form's `FormApi`, not a wrapper: the
25
- * engine is a form library, and everything mid-edit — values, dirty state,
26
- * errors, async validation, submit lifecycle — is form state, read off
25
+ * engine is a form library, and everything mid-edit - values, dirty state,
26
+ * errors, async validation, submit lifecycle - is form state, read off
27
27
  * `form.store` with the same selector idiom as the rest of the grid.
28
28
  *
29
29
  * The alias erases Form's validator generics the way `TMDataGridRowData`
@@ -31,20 +31,20 @@ export type TMDataGridEditMode = "cell" | "cellConfirm" | "row" | "batch";
31
31
  */
32
32
  export type TMDataGridRowEditForm = AnyFormApi;
33
33
 
34
- /** The live field a cell editor binds to — TanStack Form's own `FieldApi`. */
34
+ /** The live field a cell editor binds to - TanStack Form's own `FieldApi`. */
35
35
  export type TMDataGridEditField = AnyFieldApi;
36
36
 
37
37
  /**
38
38
  * A validator as TanStack Form takes it: a Standard Schema (Zod, Valibot,
39
39
  * ArkType…) or a plain function returning an error or nothing. Forwarded
40
- * untouched — the vocabulary is Form's, not a grid re-implementation.
40
+ * untouched - the vocabulary is Form's, not a grid re-implementation.
41
41
  */
42
42
  export type TMDataGridValidator =
43
43
  | StandardSchemaV1<unknown, unknown>
44
44
  | ((args: { value: never; fieldApi: never }) => unknown);
45
45
 
46
46
  /**
47
- * `meta.validate` — per-column field validators. A bare schema or function is
47
+ * `meta.validate` - per-column field validators. A bare schema or function is
48
48
  * shorthand for `{ onChange: it }`; the object form takes every trigger
49
49
  * TanStack Form defines, `Async` variants and debounce included.
50
50
  */
@@ -62,7 +62,7 @@ export type TMDataGridFieldValidate =
62
62
  };
63
63
 
64
64
  /**
65
- * `rowValidators` — form-level validators for the whole row, where
65
+ * `rowValidators` - form-level validators for the whole row, where
66
66
  * cross-field rules live. Passed into each row form's `validators` untouched;
67
67
  * pathed issues land on the matching fields, pathless ones on the row.
68
68
  */
@@ -82,7 +82,7 @@ export type TMDataGridRowValidators = {
82
82
  export type TMDataGridEditChange = {
83
83
  /** Column the field maps back to, for consumers thinking in columns. */
84
84
  columnId: string;
85
- /** The data path — `getEditFieldName` of the column. */
85
+ /** The data path - `getEditFieldName` of the column. */
86
86
  field: string;
87
87
  previous: unknown;
88
88
  next: unknown;
@@ -90,23 +90,23 @@ export type TMDataGridEditChange = {
90
90
 
91
91
  export type TMDataGridEditCommitArgs<TData extends RowData> = {
92
92
  rowId: string;
93
- /** The whole row as edited — for the consumer who saves records. */
93
+ /** The whole row as edited - for the consumer who saves records. */
94
94
  value: TData;
95
95
  /** The row as it was when editing began. */
96
96
  original: TData;
97
- /** Per-field diff — for the consumer who PATCHes. One entry in cell mode. */
97
+ /** Per-field diff - for the consumer who PATCHes. One entry in cell mode. */
98
98
  changes: Array<TMDataGridEditChange>;
99
99
  /** Which policy committed. */
100
100
  source: TMDataGridEditMode;
101
101
  };
102
102
 
103
- /** What one open row's form looks like from outside — the cell markers. */
103
+ /** What one open row's form looks like from outside - the cell markers. */
104
104
  export type TMDataGridEditRowProjection = {
105
105
  /** Field names whose value differs from the original. */
106
106
  dirtyFields: ReadonlyArray<string>;
107
107
  /** Field names carrying a validation error. */
108
108
  errorFields: ReadonlyArray<string>;
109
- /** A row-level error — a pathless `.refine()`, or a rejected commit. */
109
+ /** A row-level error - a pathless `.refine()`, or a rejected commit. */
110
110
  hasRowError: boolean;
111
111
  isSubmitting: boolean;
112
112
  };
@@ -115,7 +115,7 @@ export type TMDataGridEditRowProjection = {
115
115
  * The grid-facing index of everything mid-edit. A projection synced from the
116
116
  * open forms' stores, so body cells subscribe to this one store for their
117
117
  * dirty/error markers instead of one store per form. Only the editor host
118
- * reads a form's store directly — it is mounted for one cell at a time.
118
+ * reads a form's store directly - it is mounted for one cell at a time.
119
119
  */
120
120
  export type TMDataGridEditState = {
121
121
  /** The cell whose editor is open; `columnId: null` is a whole row (row mode). */
@@ -123,9 +123,9 @@ export type TMDataGridEditState = {
123
123
  /** Rows with a live form. In cell mode at most one; in batch, many. */
124
124
  openRowIds: ReadonlyArray<string>;
125
125
  rows: Record<string, TMDataGridEditRowProjection>;
126
- /** Phase 4 — rows being created, not yet in `data`. */
126
+ /** Phase 4 - rows being created, not yet in `data`. */
127
127
  newRows: ReadonlyArray<{ tempId: string }>;
128
- /** Phase 4 — rows marked deleted under batch mode. */
128
+ /** Phase 4 - rows marked deleted under batch mode. */
129
129
  deletedRowIds: ReadonlyArray<string>;
130
130
  };
131
131
 
@@ -141,14 +141,14 @@ type ErasedRow = Row<TMDataGridFeatures, TMDataGridRowData>;
141
141
  type ErasedColumn = Column<TMDataGridFeatures, TMDataGridRowData, unknown>;
142
142
 
143
143
  /**
144
- * What a cell editor is handed — deliberately both vocabularies at once. The
144
+ * What a cell editor is handed - deliberately both vocabularies at once. The
145
145
  * form side is TanStack Form's real `field` API (`field.state.value`,
146
146
  * `field.state.meta.errors`, `field.handleChange`, `field.handleBlur`), so a
147
147
  * custom calendar, slider or async combobox binds to it exactly as it would
148
148
  * inside any TanStack Form. The table side is where the editor is standing.
149
149
  *
150
150
  * The built-ins are implemented against this same contract, so
151
- * `meta.editor` is not a special case — it is the slot the defaults
151
+ * `meta.editor` is not a special case - it is the slot the defaults
152
152
  * fill, and the exported built-ins can be wrapped instead of replaced.
153
153
  */
154
154
  export type TMDataGridEditorArgs = {
@@ -160,9 +160,9 @@ export type TMDataGridEditorArgs = {
160
160
  row: ErasedRow;
161
161
  column: ErasedColumn;
162
162
  table: TMDataGridTable<TMDataGridRowData>;
163
- /** What Enter would do — commit the edit. For the editor's own UI. */
163
+ /** What Enter would do - commit the edit. For the editor's own UI. */
164
164
  commit: () => Promise<boolean>;
165
- /** What Escape would do — drop the draft. */
165
+ /** What Escape would do - drop the draft. */
166
166
  cancel: () => void;
167
167
  size: TMDataGridSize;
168
168
  /**
@@ -171,33 +171,33 @@ export type TMDataGridEditorArgs = {
171
171
  */
172
172
  autoFocus: boolean;
173
173
  /**
174
- * Set when typing opened the editor (the Sheets gesture) — the built-ins
174
+ * Set when typing opened the editor (the Sheets gesture) - the built-ins
175
175
  * replace the value with it and keep typing. A custom editor may ignore it.
176
176
  */
177
177
  seedText?: string;
178
178
  };
179
179
 
180
180
  /**
181
- * `meta.editor` — replaces the built-in editor for this column. Rendered as
181
+ * `meta.editor` - replaces the built-in editor for this column. Rendered as
182
182
  * JSX, never invoked as a bare function, so hooks are legal inside. Define it
183
183
  * at module scope: a new identity per render remounts the editor mid-edit.
184
184
  */
185
185
  export type TMDataGridEditorComponent = ComponentType<TMDataGridEditorArgs>;
186
186
 
187
- /** A new row being committed — `onRowAdd`, and `submitAll`'s `added`. */
187
+ /** A new row being committed - `onRowAdd`, and `submitAll`'s `added`. */
188
188
  export type TMDataGridRowAddArgs<TData extends RowData> = {
189
189
  /** The engine's placeholder id; the real id is the consumer's to mint. */
190
190
  tempId: string;
191
191
  value: TData;
192
192
  };
193
193
 
194
- /** A deletion — `onRowDelete`. */
194
+ /** A deletion - `onRowDelete`. */
195
195
  export type TMDataGridRowDeleteArgs<TData extends RowData> = {
196
196
  rowId: string;
197
197
  row: Row<TMDataGridFeatures, TData>;
198
198
  };
199
199
 
200
- /** What `submitAll` hands `onEditCommitBatch` — everything pending at once. */
200
+ /** What `submitAll` hands `onEditCommitBatch` - everything pending at once. */
201
201
  export type TMDataGridEditCommitBatchArgs<TData extends RowData> = {
202
202
  /** Every valid dirty existing row. */
203
203
  rows: Array<TMDataGridEditCommitArgs<TData>>;
@@ -207,7 +207,7 @@ export type TMDataGridEditCommitBatchArgs<TData extends RowData> = {
207
207
  deleted: Array<string>;
208
208
  };
209
209
 
210
- /** What the engine reads fresh on every call — see `createEditEngine`. */
210
+ /** What the engine reads fresh on every call - see `createEditEngine`. */
211
211
  export type TMDataGridEditEngineContext = {
212
212
  table: TMDataGridTable<TMDataGridRowData>;
213
213
  editMode: TMDataGridEditMode;
@@ -236,7 +236,7 @@ export type TMDataGridEditEngineContext = {
236
236
  * nested rows work for free: `accessorKey: "address.city"` edits
237
237
  * `values.address.city`. A column built on `accessorFn` has no path and is
238
238
  * not editable unless `meta.editField` names one. TanStack's default column
239
- * id turns dots into underscores — which is why this starts from
239
+ * id turns dots into underscores - which is why this starts from
240
240
  * `accessorKey`, never from `id`.
241
241
  */
242
242
  export function getEditFieldName(column: {
@@ -250,7 +250,7 @@ export function getEditFieldName(column: {
250
250
  return typeof accessorKey === "string" ? accessorKey : null;
251
251
  }
252
252
 
253
- /** What Delete writes into a cell — the type's honest empty value. */
253
+ /** What Delete writes into a cell - the type's honest empty value. */
254
254
  export function clearedValueForType(type: TMDataGridColumnType): unknown {
255
255
  switch (type) {
256
256
  case "string":
@@ -265,14 +265,14 @@ export function clearedValueForType(type: TMDataGridColumnType): unknown {
265
265
  }
266
266
 
267
267
  /**
268
- * The engine plus its store — `api.edit`.
268
+ * The engine plus its store - `api.edit`.
269
269
  *
270
270
  * "One row, one form": `getForm` hands out the same `FormApi` the inline
271
271
  * editors write through, so a consumer can render it in a drawer or a detail
272
272
  * panel and share values, dirty state and errors with the cells.
273
273
  */
274
274
  export type TMDataGridEditApi = {
275
- /** The projection store — subscribe with `useSelector(edit.store, …)`. */
275
+ /** The projection store - subscribe with `useSelector(edit.store, …)`. */
276
276
  store: Store<TMDataGridEditState>;
277
277
  /** Current snapshot, for reads outside React. */
278
278
  readonly state: TMDataGridEditState;
@@ -283,12 +283,12 @@ export type TMDataGridEditApi = {
283
283
  * switched it off, and the row takes edits at all.
284
284
  */
285
285
  canEditCell: (row: ErasedRow, column: ErasedColumn) => boolean;
286
- /** Whether the row takes edits at all — the edit lane's pencil gate. */
286
+ /** Whether the row takes edits at all - the edit lane's pencil gate. */
287
287
  canEditRow: (row: ErasedRow) => boolean;
288
288
  /** Opens an editor. `columnId: null` opens the whole row (row mode). */
289
289
  begin: (target: { rowId: string; columnId: string | null }) => void;
290
290
  /**
291
- * Commits one row — `form.handleSubmit` under the hood. Resolves `true`
291
+ * Commits one row - `form.handleSubmit` under the hood. Resolves `true`
292
292
  * when the form is gone: validation passed and `onEditCommit` resolved, or
293
293
  * there was nothing to save. `false` keeps the form open with its errors.
294
294
  */
@@ -296,31 +296,31 @@ export type TMDataGridEditApi = {
296
296
  /** Drops one row's draft. */
297
297
  cancel: (rowId: string) => void;
298
298
  /**
299
- * Closes the editor without touching the draft — what blur does under
299
+ * Closes the editor without touching the draft - what blur does under
300
300
  * `"cellConfirm"`, where the dirty cell keeps waiting for its ✓.
301
301
  */
302
302
  deactivate: () => void;
303
303
  /** Drops every draft. */
304
304
  cancelAll: () => void;
305
- /** Commits every open row — batch mode's save. `true` when all landed. */
305
+ /** Commits every open row - batch mode's save. `true` when all landed. */
306
306
  submitAll: () => Promise<boolean>;
307
- /** Writes the type's empty value into a cell and commits it — Delete. */
307
+ /** Writes the type's empty value into a cell and commits it - Delete. */
308
308
  clearCell: (rowId: string, columnId: string) => Promise<boolean>;
309
309
  /**
310
310
  * Opens a new entry row (the sticky block under the header) seeded from
311
- * `newRowDefaults`. Returns its temporary id — a form with no backing row
311
+ * `newRowDefaults`. Returns its temporary id - a form with no backing row
312
312
  * yet. Committing it calls `onRowAdd` (immediate modes) or joins
313
313
  * `submitAll`'s `added` (batch).
314
314
  */
315
315
  addRow: () => string;
316
316
  /**
317
317
  * Deletes a row: `onRowDelete` straight away under the immediate modes;
318
- * under batch it toggles the id in `deletedRowIds` — the row renders
318
+ * under batch it toggles the id in `deletedRowIds` - the row renders
319
319
  * struck through until `submitAll` reports it. On an uncommitted entry
320
320
  * row it just discards the entry.
321
321
  */
322
322
  deleteRow: (rowId: string) => void;
323
- /** Whether delete chrome makes sense — the lane's trash gate. */
323
+ /** Whether delete chrome makes sense - the lane's trash gate. */
324
324
  canDeleteRows: () => boolean;
325
325
  };
326
326
 
@@ -366,7 +366,7 @@ function hasAnyError(errors: ReadonlyArray<unknown>): boolean {
366
366
  * sees the latest table, mode and consumer callbacks without being rebuilt.
367
367
  *
368
368
  * Virtualization, per the plan: nothing here is DOM. A form is a plain object
369
- * in a map keyed by rowId — scroll its row away and the editor unmounts, the
369
+ * in a map keyed by rowId - scroll its row away and the editor unmounts, the
370
370
  * form keeps its values, meta and errors; scroll back and the editor
371
371
  * re-mounts over the same form.
372
372
  */
@@ -378,11 +378,11 @@ export function createEditEngine(
378
378
  type FormEntry = {
379
379
  form: TMDataGridRowEditForm;
380
380
  original: TMDataGridRowData;
381
- /** An entry-block row — a form with no backing row yet. */
381
+ /** An entry-block row - a form with no backing row yet. */
382
382
  isNew: boolean;
383
383
  /** Set by the wrapped onSubmit when the consumer's commit resolved. */
384
384
  lastSubmitOk: boolean;
385
- /** A commit already running — Enter and blur race on the same edit. */
385
+ /** A commit already running - Enter and blur race on the same edit. */
386
386
  pendingCommit: Promise<boolean> | null;
387
387
  unsubscribe: () => void;
388
388
  unmount: () => void;
@@ -392,7 +392,7 @@ export function createEditEngine(
392
392
 
393
393
  /**
394
394
  * While `submitAll` runs with an `onEditCommitBatch`, each row's wrapped
395
- * onSubmit contributes its args here instead of calling `onEditCommit` —
395
+ * onSubmit contributes its args here instead of calling `onEditCommit` -
396
396
  * validation stays per row (Form's), the consumer call becomes one.
397
397
  */
398
398
  let batchCollector: Array<TMDataGridEditCommitArgs<TMDataGridRowData>> | null =
@@ -432,7 +432,7 @@ export function createEditEngine(
432
432
  const errorFields = Object.entries(state.fieldMeta)
433
433
  .filter(
434
434
  ([name, meta]) =>
435
- // The empty path is a pathless (row-level) issue's landing spot —
435
+ // The empty path is a pathless (row-level) issue's landing spot -
436
436
  // it belongs to `hasRowError`, not to any cell's marker.
437
437
  name !== "" &&
438
438
  hasAnyError((meta as { errors: ReadonlyArray<unknown> }).errors),
@@ -593,7 +593,7 @@ export function createEditEngine(
593
593
  // Enter and blur race on the same edit; the second caller joins the
594
594
  // first's commit instead of submitting the row twice.
595
595
  if (entry.pendingCommit !== null) return entry.pendingCommit;
596
- // A pristine form has nothing to say — drop it without a consumer call.
596
+ // A pristine form has nothing to say - drop it without a consumer call.
597
597
  // Not for a new row: adding an untouched entry is still an add.
598
598
  if (
599
599
  !entry.isNew &&
@@ -641,8 +641,8 @@ export function createEditEngine(
641
641
  //
642
642
  // | Mode | Another row open |
643
643
  // | ---- | ---------------- |
644
- // | cell | committed — Sheets; a failed commit keeps holding the edit |
645
- // | row | dropped if pristine, otherwise the pencil is refused — a
644
+ // | cell | committed - Sheets; a failed commit keeps holding the edit |
645
+ // | row | dropped if pristine, otherwise the pencil is refused - a
646
646
  // row edit saves explicitly, so leaving must not save quietly |
647
647
  // | cellConfirm, batch | accumulates; every draft waits for its ✓ |
648
648
  if (openElsewhere !== undefined) {
@@ -702,7 +702,7 @@ export function createEditEngine(
702
702
  }
703
703
  const context = getContext();
704
704
  if (context.editMode === "batch") {
705
- // A toggle: the second press unmarks — the mark is a draft too.
705
+ // A toggle: the second press unmarks - the mark is a draft too.
706
706
  store.setState((prev) => ({
707
707
  ...prev,
708
708
  deletedRowIds: prev.deletedRowIds.includes(rowId)
@@ -739,7 +739,7 @@ export function createEditEngine(
739
739
 
740
740
  const submitAll = async (): Promise<boolean> => {
741
741
  if (getContext().onEditCommitBatch === undefined) {
742
- // The default: the per-row loop — edits through `onEditCommit`, entry
742
+ // The default: the per-row loop - edits through `onEditCommit`, entry
743
743
  // rows through `onRowAdd`, marked deletions through `onRowDelete`.
744
744
  const results = await Promise.all(
745
745
  [...forms.keys()].map((rowId) => commit(rowId)),
@@ -755,7 +755,7 @@ export function createEditEngine(
755
755
 
756
756
  // One consumer call for the lot. Validation stays Form's, per row: rows
757
757
  // that fail keep their forms and markers; the valid ones travel
758
- // together, and only a resolved batch drops them — a rejected save keeps
758
+ // together, and only a resolved batch drops them - a rejected save keeps
759
759
  // every draft, deletions included.
760
760
  const collected: Array<TMDataGridEditCommitArgs<TMDataGridRowData>> = [];
761
761
  const added: Array<TMDataGridRowAddArgs<TMDataGridRowData>> = [];
@@ -844,7 +844,7 @@ export function createEditEngine(
844
844
 
845
845
  /**
846
846
  * `meta.validate` normalised to the object TanStack Form's `validators`
847
- * option takes — a bare schema or function is shorthand for `{ onChange }`.
847
+ * option takes - a bare schema or function is shorthand for `{ onChange }`.
848
848
  */
849
849
  export function normalizeFieldValidate(
850
850
  validate: TMDataGridFieldValidate | undefined,
@@ -6,8 +6,8 @@ import type { TMDataGridFeatures } from "../useTMDataGrid";
6
6
  *
7
7
  * TanStack keeps one `expanded` state, and the grid opens two unrelated things
8
8
  * out of it: a group row opens into its children, a data row opens into its
9
- * detail panel. So `toggleAllRowsExpanded` — which writes the state's
10
- * whole-table form — is the wrong verb for either control. "Expand all groups"
9
+ * detail panel. So `toggleAllRowsExpanded` - which writes the state's
10
+ * whole-table form - is the wrong verb for either control. "Expand all groups"
11
11
  * in the tree menu would open every panel in the grid, and the details lane's
12
12
  * chevron would unfold the whole tree.
13
13
  *
@@ -25,7 +25,7 @@ function isTarget<TData extends RowData>(
25
25
 
26
26
  /**
27
27
  * The state as a map. `true` is the whole-table form, which has to be written
28
- * out before one kind of row can be taken back out of it — the same thing
28
+ * out before one kind of row can be taken back out of it - the same thing
29
29
  * TanStack does before toggling a single row.
30
30
  */
31
31
  function toExpandedMap<TData extends RowData>(
@@ -40,7 +40,7 @@ function toExpandedMap<TData extends RowData>(
40
40
 
41
41
  export type TMDataGridExpandAllArgs<TData extends RowData> = {
42
42
  /**
43
- * Every row in the model, groups and records alike — `flatRows` off any of
43
+ * Every row in the model, groups and records alike - `flatRows` off any of
44
44
  * them. A grouped model lists its leaves both under their group and in the
45
45
  * flat list, so this may repeat rows; both helpers are written so that a
46
46
  * repeat costs nothing.
@@ -51,7 +51,7 @@ export type TMDataGridExpandAllArgs<TData extends RowData> = {
51
51
  };
52
52
 
53
53
  /**
54
- * Whether every row of this kind is open — what the control shows, and what a
54
+ * Whether every row of this kind is open - what the control shows, and what a
55
55
  * bare toggle inverts.
56
56
  *
57
57
  * `false` when there are none of them, matching TanStack's own
@@ -8,13 +8,13 @@ import type { TMDataGridLabels } from "./labels";
8
8
  import type { TMDataGridSize } from "./sizes";
9
9
 
10
10
  /**
11
- * What a filter control is handed — the value slot of one filter-panel row.
11
+ * What a filter control is handed - the value slot of one filter-panel row.
12
12
  *
13
13
  * The contract is value-only: the control reads `operator` to shape itself
14
14
  * (a `between` pair renders two ends, an `equals` a single input) and calls
15
15
  * `onChange` with the bare value; the grid composes the operator-aware
16
16
  * `TMDataGridFilterValue` around it. A control never builds that wrapper and
17
- * never writes the operator — `table` is the escape hatch for the rare
17
+ * never writes the operator - `table` is the escape hatch for the rare
18
18
  * control that must.
19
19
  */
20
20
  export type TMDataGridFilterControlArgs = {
@@ -22,13 +22,13 @@ export type TMDataGridFilterControlArgs = {
22
22
  table: TMDataGridTable<TMDataGridRowData>;
23
23
  /** The row's currently selected operator. Read-only for the control. */
24
24
  operator: TMDataGridFilterOperator;
25
- /** The bare filter value — never the `{ operator, value }` wrapper. */
25
+ /** The bare filter value - never the `{ operator, value }` wrapper. */
26
26
  value: string | ReadonlyArray<string>;
27
27
  /** Writes the bare value; the grid pairs it with the current operator. */
28
28
  onChange: (next: string | ReadonlyArray<string>) => void;
29
29
  /**
30
30
  * The column's options through `resolveColumnOptions`, pre-resolved for a
31
- * column that declares `meta.options` or is select-shaped. Empty otherwise —
31
+ * column that declares `meta.options` or is select-shaped. Empty otherwise -
32
32
  * a control wanting faceted values on other columns resolves them itself.
33
33
  */
34
34
  options: ReadonlyArray<TMDataGridOption>;
@@ -37,7 +37,7 @@ export type TMDataGridFilterControlArgs = {
37
37
  };
38
38
 
39
39
  /**
40
- * `meta.filterControl` — replaces the built-in value control for this column.
40
+ * `meta.filterControl` - replaces the built-in value control for this column.
41
41
  * Rendered as JSX, never invoked as a bare function, so hooks are legal
42
42
  * inside. Define it at module scope so its identity is stable across renders.
43
43
  */
@@ -5,12 +5,12 @@ import type { Row, RowData, TableFeatures } from "@tanstack/react-table";
5
5
  *
6
6
  * TanStack resolves `filterFn` statically per column, so the operator travels
7
7
  * inside the filter *value* instead. That keeps the whole filter model plain,
8
- * serialisable JSON — which is what makes it portable to a server-side
8
+ * serialisable JSON - which is what makes it portable to a server-side
9
9
  * `manualFiltering` table (just forward `columnFilters` to the API).
10
10
  *
11
11
  * `value` is a string array under `isAnyOf` / `isNoneOf` (the set the cell is
12
12
  * tested against), a `[min, max]` pair under `between` (an empty string means
13
- * that end is open), and a single string everywhere else — dates travel as
13
+ * that end is open), and a single string everywhere else - dates travel as
14
14
  * ISO `YYYY-MM-DD` strings, booleans as `"true"` / `"false"`. Still plain
15
15
  * JSON.
16
16
  */
@@ -176,21 +176,21 @@ export function operatorNeedsValue(operator: TMDataGridFilterOperator): boolean
176
176
  return !VALUELESS_OPERATORS.includes(operator);
177
177
  }
178
178
 
179
- /** Whether the operator's value is a string array — `isAnyOf` / `isNoneOf`. */
179
+ /** Whether the operator's value is a string array - `isAnyOf` / `isNoneOf`. */
180
180
  export function operatorTakesArrayValue(
181
181
  operator: TMDataGridFilterOperator,
182
182
  ): boolean {
183
183
  return ARRAY_OPERATORS.includes(operator);
184
184
  }
185
185
 
186
- /** Whether the operator's value is a `[min, max]` pair — `between`. */
186
+ /** Whether the operator's value is a `[min, max]` pair - `between`. */
187
187
  export function operatorTakesRangeValue(
188
188
  operator: TMDataGridFilterOperator,
189
189
  ): boolean {
190
190
  return RANGE_OPERATORS.includes(operator);
191
191
  }
192
192
 
193
- /** The untouched value a fresh filter starts with — the operator's shape, empty. */
193
+ /** The untouched value a fresh filter starts with - the operator's shape, empty. */
194
194
  export function emptyValueForOperator(
195
195
  operator: TMDataGridFilterOperator,
196
196
  ): string | ReadonlyArray<string> {
@@ -228,8 +228,8 @@ export function isFilterActive(value: unknown): boolean {
228
228
  /**
229
229
  * One-line description of a single filter, as shown on a filter pill.
230
230
  *
231
- * The type's default operator is left implicit — "First name: Sofia" reads the
232
- * way a person would say it — while any other operator is spelled out, since
231
+ * The type's default operator is left implicit - "First name: Sofia" reads the
232
+ * way a person would say it - while any other operator is spelled out, since
233
233
  * that is the part a reader cannot guess.
234
234
  */
235
235
  export function formatFilterLabel({
@@ -241,7 +241,7 @@ export function formatFilterLabel({
241
241
  label: string;
242
242
  type: TMDataGridColumnType;
243
243
  filter: TMDataGridFilterValue;
244
- /** Localized operator names — `labels.operators`. Defaults to English. */
244
+ /** Localized operator names - `labels.operators`. Defaults to English. */
245
245
  operatorLabels?: Record<TMDataGridFilterOperator, string>;
246
246
  }): string {
247
247
  const { operator, value } = filter;
@@ -260,8 +260,8 @@ export function formatFilterLabel({
260
260
 
261
261
  /**
262
262
  * A value's calendar day as `YYYY-MM-DD`, or `null` for anything that has no
263
- * day to speak of. A `Date` is read in local time — `sv-SE` formats as ISO,
264
- * which is the shape `<input type="date">` produced on the other side — and a
263
+ * day to speak of. A `Date` is read in local time - `sv-SE` formats as ISO,
264
+ * which is the shape `<input type="date">` produced on the other side - and a
265
265
  * string contributes its leading ISO day, so both `2026-07-31` and a full
266
266
  * timestamp compare by day.
267
267
  */
@@ -317,8 +317,8 @@ export function matchesFilter(
317
317
  if (operator === "isEmpty") return isEmptyCell(cellValue);
318
318
  if (operator === "isNotEmpty") return !isEmptyCell(cellValue);
319
319
 
320
- // Set membership. The cell side is also a list — a multiSelect cell holds an
321
- // array — so "any of" is an intersection test and "none of" its complement.
320
+ // Set membership. The cell side is also a list - a multiSelect cell holds an
321
+ // array - so "any of" is an intersection test and "none of" its complement.
322
322
  if (operator === "isAnyOf" || operator === "isNoneOf") {
323
323
  const set = (Array.isArray(value) ? value : [value])
324
324
  .map((entry) => String(entry).toLowerCase())
@@ -332,7 +332,7 @@ export function matchesFilter(
332
332
  }
333
333
 
334
334
  // A closed or half-open interval. Each present end must hold; an empty end
335
- // is open — and a fully empty pair matches everything, the same way a blank
335
+ // is open - and a fully empty pair matches everything, the same way a blank
336
336
  // needle does. Ends compare the way the single-ended operators would:
337
337
  // numerically when both sides are numbers, by calendar day otherwise.
338
338
  if (operator === "between") {
@@ -349,7 +349,7 @@ export function matchesFilter(
349
349
  if (filterText === "") return true;
350
350
 
351
351
  // Day ordering. ISO days compare correctly as strings, which keeps this
352
- // free of timezone arithmetic — the whole comparison lives in local days.
352
+ // free of timezone arithmetic - the whole comparison lives in local days.
353
353
  switch (operator) {
354
354
  case "before":
355
355
  case "after":
@@ -4,7 +4,7 @@ import {
4
4
  } from "./filterOperators";
5
5
 
6
6
  /**
7
- * Every user-facing string in the grid — menu items, panels, tooltips, the
7
+ * Every user-facing string in the grid - menu items, panels, tooltips, the
8
8
  * pager, and the `aria-label`s a screen reader speaks. One flat object, so an
9
9
  * override is a spread rather than a deep merge; `operators` is the single
10
10
  * nested record and {@link mergeLabels} folds it separately.
@@ -17,9 +17,9 @@ export type TMDataGridLabels = {
17
17
  operators: Record<TMDataGridFilterOperator, string>;
18
18
 
19
19
  // Toolbar
20
- /** "Manage columns" — the burger button and the header menu item. */
20
+ /** "Manage columns" - the burger button and the header menu item. */
21
21
  manageColumns: string;
22
- /** "Filters" — the funnel button and the filter panel's title. */
22
+ /** "Filters" - the funnel button and the filter panel's title. */
23
23
  filters: string;
24
24
  searchPlaceholder: string;
25
25
  searchLabel: string;
@@ -35,7 +35,7 @@ export type TMDataGridLabels = {
35
35
  /** Menu/panel name of the generated row-number gutter. */
36
36
  rowNumberColumnLabel: string;
37
37
  /**
38
- * The truly-empty message — no data and no filters. Filtered-empty says
38
+ * The truly-empty message - no data and no filters. Filtered-empty says
39
39
  * {@link noResults} instead; only one of the two is the user's own doing.
40
40
  */
41
41
  noRows: string;
@@ -51,7 +51,7 @@ export type TMDataGridLabels = {
51
51
  filterTo: string;
52
52
  /** `DgTriStateFilter`'s no-filter segment. */
53
53
  filterAll: string;
54
- /** What a boolean column's `true` reads as — its filter choice and cell text. */
54
+ /** What a boolean column's `true` reads as - its filter choice and cell text. */
55
55
  booleanTrue: string;
56
56
  booleanFalse: string;
57
57
  addFilter: string;
@@ -115,9 +115,9 @@ export type TMDataGridLabels = {
115
115
  saveAllEdits: (rows: number) => string;
116
116
  /** `EditActions`' Discard. */
117
117
  discardAllEdits: string;
118
- /** The entry row's ✓ — commit the add. */
118
+ /** The entry row's ✓ - commit the add. */
119
119
  confirmNewRow: string;
120
- /** The entry row's ✕ — drop the entry. */
120
+ /** The entry row's ✕ - drop the entry. */
121
121
  discardNewRow: string;
122
122
  /** The lane's trash can. */
123
123
  deleteRow: string;
@@ -152,7 +152,7 @@ export type TMDataGridLabels = {
152
152
 
153
153
  /**
154
154
  * What the `labels` option accepts: any subset, merged over the English
155
- * defaults — so `{ noResults: "Inga rader" }` is a complete configuration.
155
+ * defaults - so `{ noResults: "Inga rader" }` is a complete configuration.
156
156
  */
157
157
  export type TMDataGridLabelsOverride = Partial<
158
158
  Omit<TMDataGridLabels, "operators">
@@ -9,7 +9,7 @@ import type { TMDataGridTable } from "../useTMDataGrid";
9
9
  export type TMDataGridMatchRange = { start: number; end: number };
10
10
 
11
11
  /**
12
- * The operators whose match is a contiguous substring — the ones highlighting
12
+ * The operators whose match is a contiguous substring - the ones highlighting
13
13
  * can point at. Equality highlights nothing: marking the whole cell says
14
14
  * nothing the filter did not already say.
15
15
  */
@@ -20,14 +20,14 @@ const HIGHLIGHTABLE_OPERATORS: ReadonlyArray<TMDataGridFilterOperator> = [
20
20
  ];
21
21
 
22
22
  /**
23
- * Every needle that could highlight in a given column, keyed by column id —
23
+ * Every needle that could highlight in a given column, keyed by column id -
24
24
  * the quick search for the columns it searches, plus any contains-family
25
25
  * column filter. `null` while nothing highlightable is active, which is the
26
26
  * common case and the cheap one.
27
27
  *
28
28
  * The fuzzy quick search still contributes its raw text: when the needle
29
29
  * occurs contiguously it is highlighted, and a typo-match simply shows no
30
- * highlight — the honest answer to what a non-contiguous match "is".
30
+ * highlight - the honest answer to what a non-contiguous match "is".
31
31
  */
32
32
  export function buildMatchNeedles<TData extends RowData>(
33
33
  table: TMDataGridTable<TData>,
@@ -62,7 +62,7 @@ export function buildMatchNeedles<TData extends RowData>(
62
62
 
63
63
  /**
64
64
  * Where the needles occur in the text, case-insensitively, merged where they
65
- * overlap or touch — so two needles covering "St" and "tock" come back as one
65
+ * overlap or touch - so two needles covering "St" and "tock" come back as one
66
66
  * range rather than nested marks. `null` when nothing matches.
67
67
  */
68
68
  export function findMatchRanges(