@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
@@ -35,7 +35,7 @@ export type TMDataGridProps<TData extends RowData> = TMDataGridApi<TData> & {
35
35
  className?: string;
36
36
  /**
37
37
  * Inline styles. Widened to accept CSS variables, which is how the grid's
38
- * colours are themed — `--dg-row-selected-bg` for the selected row
38
+ * colours are themed - `--dg-row-selected-bg` for the selected row
39
39
  * background. Plain `CSSProperties` rejects `--*` keys.
40
40
  */
41
41
  style?: CSSProperties & Record<`--${string}`, string | number>;
@@ -44,7 +44,7 @@ export type TMDataGridProps<TData extends RowData> = TMDataGridApi<TData> & {
44
44
  /**
45
45
  * Names this grid for tests. The grid names its own pieces with
46
46
  * `data-dg-part` rather than minting test ids, and those repeat across
47
- * grids — scope through this one and they stop colliding:
47
+ * grids - scope through this one and they stop colliding:
48
48
  *
49
49
  * ```ts
50
50
  * page.getByTestId("orders").locator('[data-dg-part="row"][data-row-id="42"]')
@@ -56,7 +56,7 @@ export type TMDataGridProps<TData extends RowData> = TMDataGridApi<TData> & {
56
56
  };
57
57
 
58
58
  /**
59
- * Root of the grid. Takes the object returned by `useTMDataGrid` — spread it —
59
+ * Root of the grid. Takes the object returned by `useTMDataGrid` - spread it -
60
60
  * and publishes it to the compound components below it:
61
61
  *
62
62
  * ```tsx
@@ -133,7 +133,7 @@ function TMDataGridRoot<TData extends RowData>({
133
133
  <div
134
134
  id={id}
135
135
  data-testid={testId}
136
- // The handle everything else scopes off — see the `data-testid` prop.
136
+ // The handle everything else scopes off - see the `data-testid` prop.
137
137
  data-dg-root
138
138
  data-size={size}
139
139
  className={[classes.root, className].filter(Boolean).join(" ")}
@@ -160,7 +160,7 @@ export const TMDataGrid = Object.assign(TMDataGridRoot, {
160
160
  FilterPanel: TMDataGridFilterPanel,
161
161
  /**
162
162
  * Takes the grid as an `api` prop rather than from context, so it can be
163
- * rendered outside `<TMDataGrid>` — a page header, for instance.
163
+ * rendered outside `<TMDataGrid>` - a page header, for instance.
164
164
  */
165
165
  FilterPills: TMDataGridFilterPills,
166
166
  /** Rendered by `TMDataGrid.ColumnsButton`; exported for custom layouts. */
@@ -42,11 +42,11 @@ const BUILT_IN_EDITORS: Record<
42
42
  export type TMDataGridCellEditorClose =
43
43
  | { committed: true; via: "enter" | "tab" | "shift-tab" | "pick" }
44
44
  | { committed: false; via: "escape" }
45
- /** Batch: the editor closed, the draft stays — Enter parks, Tab moves on. */
45
+ /** Batch: the editor closed, the draft stays - Enter parks, Tab moves on. */
46
46
  | { committed: false; via: "defer" | "defer-tab" | "defer-shift-tab" };
47
47
 
48
48
  /**
49
- * The host an editing cell mounts — it owns the field, the keys and the blur
49
+ * The host an editing cell mounts - it owns the field, the keys and the blur
50
50
  * policy; the editor inside it (a built-in picked by `meta.type`, or the
51
51
  * column's `meta.editor`) owns the input.
52
52
  *
@@ -66,7 +66,7 @@ export function TMDataGridCellEditor({
66
66
  takeSeedText: () => string | undefined;
67
67
  /** In row mode only the first editable cell takes the focus. */
68
68
  autoFocus?: boolean;
69
- /** After the editor closed itself — the table moves the focus. */
69
+ /** After the editor closed itself - the table moves the focus. */
70
70
  onClose: (args: TMDataGridCellEditorClose) => void;
71
71
  }) {
72
72
  const { table, edit, features, labels, controlSize } = useTMDataGridContext();
@@ -164,7 +164,7 @@ export function TMDataGridCellEditor({
164
164
  };
165
165
 
166
166
  /**
167
- * Focus left the editor entirely — a click somewhere else. Under `"cell"`
167
+ * Focus left the editor entirely - a click somewhere else. Under `"cell"`
168
168
  * that commits (Sheets); a commit blocked by validation keeps the form and
169
169
  * its invalid marker, with the editor closed. The confirming modes keep
170
170
  * the draft open-but-inactive, waiting for their explicit ✓.
@@ -82,7 +82,7 @@ export function TMDataGridColumnsPanel() {
82
82
  />
83
83
  ))}
84
84
  {visibleInPanel.length === 0 && (
85
- <Text size={controlSize} c="dimmed">
85
+ <Text span size={controlSize} c="dimmed">
86
86
  {labels.columnsNoMatch(search)}
87
87
  </Text>
88
88
  )}
@@ -102,7 +102,7 @@ export function TMDataGridColumnsPanel() {
102
102
  />
103
103
  {/* The whole layout, not only visibility: one reset with an honest
104
104
  scope, stated in the tooltip. `table.resetColumnVisibility()` would
105
- also be wrong under persistence — it resets to `initialState`,
105
+ also be wrong under persistence - it resets to `initialState`,
106
106
  which the mount built *from* the persisted payload. */}
107
107
  <Tooltip label={labels.columnsResetHint} withArrow>
108
108
  <Button
@@ -31,7 +31,7 @@ function DetailsCell<TData extends RowData>({
31
31
  // cell, not by Tab, or the grid would have one tab stop per mounted row.
32
32
  const tabIndex = useCellControlTabIndex();
33
33
 
34
- // Group rows open into their children, not into a panel — see the body's
34
+ // Group rows open into their children, not into a panel - see the body's
35
35
  // `showsDetails`. Their cells are aggregated, so this lane is already blank
36
36
  // on them; the guard is what makes that a decision rather than a side effect.
37
37
  if (row.getIsGrouped()) return null;
@@ -63,7 +63,7 @@ function DetailsCell<TData extends RowData>({
63
63
  * selects and clears every row.
64
64
  *
65
65
  * Deliberately not `table.toggleAllRowsExpanded()`: that writes the `expanded`
66
- * state's whole-table form, and one state holds both the tree and the panels —
66
+ * state's whole-table form, and one state holds both the tree and the panels -
67
67
  * so it would unfold every group as well. Only the data rows are touched here;
68
68
  * whatever the tree was showing, it goes on showing. See resolveExpandAll.
69
69
  */
@@ -94,7 +94,7 @@ function DetailsHeader<TData extends RowData>({
94
94
  }
95
95
  data-dg-part="details-toggle-all"
96
96
  // Partly expanded opens the rest rather than closing what is already
97
- // open — the same reading as an indeterminate select-all box.
97
+ // open - the same reading as an indeterminate select-all box.
98
98
  onClick={() =>
99
99
  table.setExpanded(
100
100
  resolveExpandAll({
@@ -118,7 +118,7 @@ function DetailsHeader<TData extends RowData>({
118
118
  *
119
119
  * Structural, like the checkbox and tree columns: fixed width, pinned to the
120
120
  * left after both of them, not hideable, not movable and not resizable. A
121
- * toggle that wandered off to the right of the grid — or hid itself — would
121
+ * toggle that wandered off to the right of the grid (or hid itself) would
122
122
  * leave rows with panels no one can open.
123
123
  *
124
124
  * Last of the three because it acts on one record: the checkbox picks rows out
@@ -140,7 +140,7 @@ export function createDetailsColumn<TData extends RowData>(
140
140
  enableOrdering: false,
141
141
  },
142
142
  // A system lane: as wide as the control it holds and no wider. Fixed at
143
- // every scale — the control does not grow with the font size, so neither
143
+ // every scale - the control does not grow with the font size, so neither
144
144
  // should its track.
145
145
  size: 36,
146
146
  minSize: 36,
@@ -157,7 +157,7 @@ export function createDetailsColumn<TData extends RowData>(
157
157
  header: ({ table }) => <DetailsHeader table={table} />,
158
158
  cell: ({ row }) => <DetailsCell row={row} />,
159
159
  // Deliberately no `aggregatedCell`: on a group row every cell outside the
160
- // grouped column counts as aggregated, and blank is the right answer here —
160
+ // grouped column counts as aggregated, and blank is the right answer here -
161
161
  // groups expand into their rows, not into a panel.
162
162
  };
163
163
  }
@@ -5,8 +5,8 @@ import { useTMDataGridContext } from "../TMDataGridContext";
5
5
  /**
6
6
  * Batch mode's toolbar chrome: Save with the dirty-row count, and Discard.
7
7
  * Both read the edit store, so they grey out while nothing is dirty and the
8
- * Save spins while a submit is in flight. Works under any `editMode` — a
9
- * cellConfirm grid accumulating drafts can offer the same pair — and renders
8
+ * Save spins while a submit is in flight. Works under any `editMode` - a
9
+ * cellConfirm grid accumulating drafts can offer the same pair - and renders
10
10
  * nothing while editing is off.
11
11
  *
12
12
  * ```tsx
@@ -21,7 +21,7 @@ export const EDIT_COLUMN_ID = "__edit__";
21
21
  * One row's slot in the edit lane. Three states, driven off the edit store:
22
22
  * a pencil while idle, Save/Cancel while the row's form is open, a loader
23
23
  * while its commit is in flight. A row error turns the Save red and puts the
24
- * message in its tooltip — the pathless `.refine()` has nowhere else to land.
24
+ * message in its tooltip - the pathless `.refine()` has nowhere else to land.
25
25
  */
26
26
  function EditLaneCell<TData extends RowData>({
27
27
  row,
@@ -79,7 +79,7 @@ function EditLaneCell<TData extends RowData>({
79
79
  );
80
80
  }
81
81
 
82
- // A batch deletion mark is a draft like any other — restorable in place.
82
+ // A batch deletion mark is a draft like any other - restorable in place.
83
83
  if (isMarkedDeleted) {
84
84
  return (
85
85
  <ActionIcon
@@ -204,7 +204,7 @@ function EditLaneCell<TData extends RowData>({
204
204
 
205
205
  /**
206
206
  * The generated edit lane, appended and pinned right under
207
- * `editMode: "row"` — the row's Save at the end of the row, mirroring the
207
+ * `editMode: "row"` - the row's Save at the end of the row, mirroring the
208
208
  * checkbox lane's build on the left.
209
209
  */
210
210
  export function createEditColumn<TData extends RowData>(
@@ -229,7 +229,7 @@ export function createEditColumn<TData extends RowData>(
229
229
  enablePinning: false,
230
230
  header: () => null,
231
231
  cell: ({ row }) => <EditLaneCell row={row} />,
232
- // Group rows: same reasoning as the checkbox lane — without this the
232
+ // Group rows: same reasoning as the checkbox lane - without this the
233
233
  // cell renders blank on aggregated rows, but here blank is also correct,
234
234
  // so the aggregated cell renders the same (null for groups).
235
235
  aggregatedCell: ({ row }) => <EditLaneCell row={row} />,
@@ -23,7 +23,7 @@ type ErasedColumn = Column<TMDataGridFeatures, TMDataGridRowData, unknown>;
23
23
 
24
24
  /**
25
25
  * Whether an entry cell edits. `edit.canEditCell` asks the main table for
26
- * the row, and an entry row is precisely not there yet — so the structural
26
+ * the row, and an entry row is precisely not there yet - so the structural
27
27
  * half of the same rule is applied to the entry table's own row.
28
28
  */
29
29
  function isEntryCellEditable(
@@ -39,13 +39,13 @@ function isEntryCellEditable(
39
39
  }
40
40
 
41
41
  /**
42
- * The sticky entry block — one row of open editors per `edit.addRow()`,
42
+ * The sticky entry block - one row of open editors per `edit.addRow()`,
43
43
  * pinned under the header. The one place stickiness is genuinely required:
44
44
  * an existing row that scrolls away has a place to scroll back to, a row
45
45
  * being typed into does not exist anywhere else.
46
46
  *
47
- * A second, tiny table instance over the new rows' seed values — same
48
- * columns, its own `useTable`, no sorting or filtering ever exercised — so
47
+ * A second, tiny table instance over the new rows' seed values - same
48
+ * columns, its own `useTable`, no sorting or filtering ever exercised - so
49
49
  * these are real `Cell`s and the editor host, the typed editors and the
50
50
  * validators all apply unchanged. The forms live in the same engine map as
51
51
  * every other draft, keyed by their temporary ids.
@@ -67,7 +67,7 @@ export function TMDataGridEntryRows({
67
67
  const newRows = useSelector(edit.store, (state) => state.newRows);
68
68
 
69
69
  // The seed values, frozen at addRow: the live values belong to the forms,
70
- // which the editors read directly — this table only provides row and cell
70
+ // which the editors read directly - this table only provides row and cell
71
71
  // identity.
72
72
  const data = useMemo(
73
73
  () =>
@@ -150,7 +150,7 @@ export function TMDataGridEntryRows({
150
150
  onClose={() => {}}
151
151
  />
152
152
  ) : cell !== undefined && column.id === EDIT_COLUMN_ID ? (
153
- // The lane's cell — the entry row's ✓/✕ pair.
153
+ // The lane's cell - the entry row's ✓/✕ pair.
154
154
  flexRender(cell.column.columnDef.cell, cell.getContext())
155
155
  ) : null}
156
156
  </div>
@@ -38,7 +38,7 @@ function asFilterValue(value: unknown): TMDataGridFilterValue {
38
38
 
39
39
  /**
40
40
  * The three value shapes an operator can take. A typed value survives an
41
- * operator or column change only within its shape — a set is not a range,
41
+ * operator or column change only within its shape - a set is not a range,
42
42
  * even though both are arrays.
43
43
  */
44
44
  function valueShape(
@@ -53,7 +53,7 @@ function valueShape(
53
53
  * The MUI-style filter surface: one row per active column filter, each row a
54
54
  * column / operator / value triple. It only ever reads and writes the table's
55
55
  * `columnFilters` state, so a `manualFiltering` grid gets the same panel for
56
- * free — the state is forwarded to the server instead of a row model.
56
+ * free - the state is forwarded to the server instead of a row model.
57
57
  */
58
58
  export function TMDataGridFilterPanel() {
59
59
  const { table, ui, labels, controlSize } = useTMDataGridContext();
@@ -110,7 +110,7 @@ export function TMDataGridFilterPanel() {
110
110
  ? getColumnDefaultOperator(target)
111
111
  : getDefaultOperator("string");
112
112
  // The typed value only survives the move while it still fits the new
113
- // operator — a text needle has no meaning to an `isAnyOf` set, or a set
113
+ // operator - a text needle has no meaning to an `isAnyOf` set, or a set
114
114
  // to a text input.
115
115
  const value =
116
116
  valueShape(operator) === valueShape(current.operator)
@@ -200,7 +200,7 @@ export function TMDataGridFilterPanel() {
200
200
  }}
201
201
  >
202
202
  <div className={classes.filterPanelHeader}>
203
- <Text size={controlSize} fw={600}>
203
+ <Text span size={controlSize} fw={600}>
204
204
  {labels.filters}
205
205
  </Text>
206
206
  <ActionIcon
@@ -222,7 +222,7 @@ export function TMDataGridFilterPanel() {
222
222
  const type = column ? getColumnType(column) : "string";
223
223
  const needsValue = operatorNeedsValue(value.operator);
224
224
  const scalarValue = typeof value.value === "string" ? value.value : "";
225
- // Pre-resolved only where options mean something out of the box —
225
+ // Pre-resolved only where options mean something out of the box -
226
226
  // a declared set, or a select-shaped column's faceted values. A
227
227
  // custom control wanting faceted values elsewhere resolves them
228
228
  // itself; resolving here would build the faceted index for every
@@ -293,7 +293,7 @@ export function TMDataGridFilterPanel() {
293
293
 
294
294
  {column ? (
295
295
  // The value slot: `meta.filterControl` if the column declares
296
- // one, the built-in shape-by-operator input otherwise — both
296
+ // one, the built-in shape-by-operator input otherwise - both
297
297
  // through the same value-only contract.
298
298
  <ValueControl
299
299
  column={column}
@@ -7,7 +7,7 @@
7
7
  gap: var(--mantine-spacing-xs);
8
8
  }
9
9
 
10
- /* Mantine's dark-scheme pill is `dark-7` — the same colour as the body — so a
10
+ /* Mantine's dark-scheme pill is `dark-7` - the same colour as the body - so a
11
11
  pill sitting on the page rather than inside a `PillsInput` disappears. Both
12
12
  schemes are restated here so the two stay a pair, with a border to keep the
13
13
  shape readable against any surface. Scoped through `.filterPills` for the
@@ -23,7 +23,7 @@
23
23
  }
24
24
 
25
25
  /* The label is a button so a pill can be clicked back open, but it must read as
26
- part of the pill — Mantine's own label styles carry the font. */
26
+ part of the pill - Mantine's own label styles carry the font. */
27
27
  .pillLabel {
28
28
  font: inherit;
29
29
  color: inherit;
@@ -27,7 +27,7 @@ export type TMDataGridFilterPillsProps<TData extends RowData> = {
27
27
  };
28
28
 
29
29
  /**
30
- * One pill per active filter — `First name: Sofia ✕` — with the ✕ clearing that
30
+ * One pill per active filter (`First name: Sofia ✕`), with the ✕ clearing that
31
31
  * filter and a click on the label opening the filter panel on its column.
32
32
  *
33
33
  * The one grid component that takes the api as a prop instead of reading
@@ -11,7 +11,7 @@
11
11
 
12
12
  /* Grouping suspends paging. The pager stays put and fades rather than
13
13
  disappearing, so the footer reads as switched off rather than as having
14
- vanished — and the row it holds does not change height under the grid. Its
14
+ vanished - and the row it holds does not change height under the grid. Its
15
15
  controls carry their own `disabled`; this is the label half. */
16
16
  .footer[data-paging-suspended="true"] {
17
17
  opacity: 0.55;
@@ -61,11 +61,11 @@ export type TMDataGridFooterProps = {
61
61
  * MUI-style pager: "Rows per page · from–to of total · ‹ ›".
62
62
  *
63
63
  * Renders nothing unless pagination is enabled (`enablePagination` or
64
- * `manualPagination`) — the footer is a pager; extra footer content belongs in
64
+ * `manualPagination`) - the footer is a pager; extra footer content belongs in
65
65
  * your own layout.
66
66
  *
67
67
  * Row totals come from `table.getRowCount()`, which prefers `options.rowCount`
68
- * — so a server-paged grid shows the server's total without changes here.
68
+ * - so a server-paged grid shows the server's total without changes here.
69
69
  */
70
70
  export function TMDataGridFooter({
71
71
  pageSizeOptions = [10, 25, 50, 100],
@@ -84,7 +84,7 @@ export function TMDataGridFooter({
84
84
  );
85
85
  }
86
86
 
87
- // Grouping suspends paging — see isPagingActive. The pager is greyed out
87
+ // Grouping suspends paging - see isPagingActive. The pager is greyed out
88
88
  // rather than dropped, so that a footer going quiet reads as a state the grid
89
89
  // is in rather than as something that broke.
90
90
  const paging = isPagingActive(table, features);
@@ -114,7 +114,13 @@ export function TMDataGridFooter({
114
114
  data-paging-suspended={!paging}
115
115
  >
116
116
  <Group gap="xs" wrap="nowrap">
117
- <Text size={controlSize} c="dimmed">
117
+ {/* `span`, here and in every other label the grid renders: Mantine's
118
+ Text is a `<p>` by default, and a grid dropped into prose - a
119
+ docs page, a CMS body, Mantine's own Typography - picks up that
120
+ context's `p` margins. A bottom margin on a flex item shifts its
121
+ border box up off centre, which is how the pager came to sit a
122
+ half-line below its own label. Chrome labels are not prose. */}
123
+ <Text span size={controlSize} c="dimmed">
118
124
  {labels.rowsPerPage}
119
125
  </Text>
120
126
  <Select
@@ -133,11 +139,11 @@ export function TMDataGridFooter({
133
139
  />
134
140
  </Group>
135
141
 
136
- <Text size={controlSize} c="dimmed" data-dg-part="page-range">
142
+ <Text span size={controlSize} c="dimmed" data-dg-part="page-range">
137
143
  {paging
138
144
  ? labels.pageRange({ from, to, total })
139
145
  : // Not a range: nothing is being sliced, so a range would be a lie.
140
- // Counted before grouping too — `getRowCount()` would be counting
146
+ // Counted before grouping too - `getRowCount()` would be counting
141
147
  // the tree, so a collapsed grid would claim to hold eight rows.
142
148
  labels.groupedAllRows(table.getFilteredRowModel().rows.length)}
143
149
  </Text>
@@ -39,7 +39,7 @@
39
39
  font-weight: 600;
40
40
  }
41
41
 
42
- /* Never squeezed out by a long group value — the count is the part that tells
42
+ /* Never squeezed out by a long group value - the count is the part that tells
43
43
  you whether the group is worth opening. */
44
44
  .groupCount {
45
45
  flex: 0 0 auto;
@@ -17,7 +17,7 @@ export const GROUP_COLUMN_ID = "__group__";
17
17
  /** Indent added per level of nesting, in px. */
18
18
  const INDENT_STEP = 16;
19
19
 
20
- /** Shown for a group whose value is empty — `String(null)` would read as "null". */
20
+ /** Shown for a group whose value is empty - `String(null)` would read as "null". */
21
21
  const BLANK_GROUP_LABEL = "(Blank)";
22
22
 
23
23
  /**
@@ -42,7 +42,7 @@ export function formatGroupValue(
42
42
  * The tree cell: chevron, group value and leaf count, indented by depth.
43
43
  *
44
44
  * Expansion is read through a subscription rather than from `row.getIsExpanded()`
45
- * in the component body for the same reason the select checkbox does it — the
45
+ * in the component body for the same reason the select checkbox does it - the
46
46
  * `row` identity survives an expand, so the React Compiler would cache the call
47
47
  * along with it and the chevron would never turn. See TMDataGridSelectColumn.
48
48
  */
@@ -119,14 +119,14 @@ function GroupHeader<TData extends RowData>({
119
119
 
120
120
  /**
121
121
  * The generated tree column, prepended whenever grouping is enabled and hidden
122
- * again while `grouping` is empty — see the visibility effect in
122
+ * again while `grouping` is empty - see the visibility effect in
123
123
  * `useTMDataGrid`.
124
124
  *
125
125
  * It exists because TanStack ships no auto group column: `groupedColumnMode:
126
126
  * "remove"` takes the grouped column out of the grid, so something has to hold
127
127
  * the tree. Modelled on the checkbox column, which is generated the same way.
128
128
  *
129
- * Not groupable itself, and nothing had to be written to make that true —
129
+ * Not groupable itself, and nothing had to be written to make that true -
130
130
  * `column.getCanGroup()` requires an `accessorFn`, which a display column has
131
131
  * no reason to have.
132
132
  */
@@ -152,7 +152,7 @@ export function createGroupColumn<TData extends RowData>(
152
152
  enablePinning: false,
153
153
  cell: ({ row }) => <GroupCell row={row} />,
154
154
  // A group row has subRows, so every cell on it that is not the grouped
155
- // column reports `getIsAggregated()` — this lane included. Without an
155
+ // column reports `getIsAggregated()` - this lane included. Without an
156
156
  // `aggregatedCell` the body would take that as "nothing to summarise" and
157
157
  // render the tree lane blank on exactly the rows it exists for.
158
158
  aggregatedCell: ({ row }) => <GroupCell row={row} />,
@@ -21,8 +21,8 @@
21
21
  justify-content: center;
22
22
  }
23
23
 
24
- /* Same as the body cells: a control lane's header — the select-all box, the
25
- expand-all chevron — sits in a fixed 36px track, which the text padding would
24
+ /* Same as the body cells: a control lane's header - the select-all box, the
25
+ expand-all chevron - sits in a fixed 36px track, which the text padding would
26
26
  squeeze it out of at the larger sizes. */
27
27
  .headerCell[data-control-column="true"] {
28
28
  padding: 0;
@@ -30,7 +30,7 @@
30
30
 
31
31
  /* A filtered column reads as active: its rows are a subset, and nothing else in
32
32
  the header says so once the filter panel is closed. A sorted one is not
33
- tinted — the arrow, pinned visible for as long as the sort lasts, already
33
+ tinted - the arrow, pinned visible for as long as the sort lasts, already
34
34
  says which column sorts and which way. */
35
35
  .headerCellFiltered .headerTitle {
36
36
  color: var(--mantine-primary-color-filled);
@@ -71,7 +71,7 @@
71
71
  margin-right: auto;
72
72
  }
73
73
 
74
- /* Idle actions are removed from layout, not just faded — reserving their width
74
+ /* Idle actions are removed from layout, not just faded - reserving their width
75
75
  would truncate header text that otherwise fits. Indicators that must stay
76
76
  visible (active sort, active filter, open menu) set data-pinned-visible. */
77
77
  .headerAction {
@@ -98,7 +98,7 @@
98
98
  opacity: 1;
99
99
  }
100
100
 
101
- /* Sort priority under multi-sort — "1", "2", … beside the arrow. Only rendered
101
+ /* Sort priority under multi-sort - "1", "2", … beside the arrow. Only rendered
102
102
  while more than one column sorts, so a single sort stays as it always was. */
103
103
  .sortIndex {
104
104
  font-size: 10px;
@@ -109,8 +109,8 @@
109
109
  text-align: center;
110
110
  }
111
111
 
112
- /* Column reordering. The dragged header stays in place and dims — the browser
113
- already carries a snapshot of it under the pointer — while the header being
112
+ /* Column reordering. The dragged header stays in place and dims - the browser
113
+ already carries a snapshot of it under the pointer - while the header being
114
114
  dragged over draws a bar on the edge the column will land against. */
115
115
  .headerCellDragging {
116
116
  opacity: 0.4;
@@ -154,7 +154,7 @@
154
154
  touch-action: none;
155
155
  }
156
156
 
157
- /* The divider is a hover affordance, not permanent chrome — it only appears on
157
+ /* The divider is a hover affordance, not permanent chrome - it only appears on
158
158
  the header cell you are pointing at, or on the column being resized. */
159
159
  .columnSeparator::after {
160
160
  content: "";
@@ -67,7 +67,7 @@ export function TMDataGridHeaderCell({
67
67
  const [contextMenuOpened, setContextMenuOpened] = useState(false);
68
68
  const [dropSide, setDropSide] = useState<TMDataGridDropSide | null>(null);
69
69
  const cellRef = useRef<HTMLDivElement>(null);
70
- // A resize drag ends with a click that bubbles to the header — without this
70
+ // A resize drag ends with a click that bubbles to the header - without this
71
71
  // every resize would also toggle the column's sort order.
72
72
  const suppressSortRef = useRef(false);
73
73
 
@@ -96,7 +96,7 @@ export function TMDataGridHeaderCell({
96
96
  const sortDirection = sorting.find((sort) => sort.id === column.id)?.desc;
97
97
  const isSorted = sortDirection !== undefined;
98
98
  // 1-based position in a multi-sort, shown only while more than one column
99
- // sorts — under a single sort the arrow already says everything.
99
+ // sorts - under a single sort the arrow already says everything.
100
100
  const sortIndex =
101
101
  isSorted && sorting.length > 1
102
102
  ? sorting.findIndex((sort) => sort.id === column.id) + 1
@@ -184,7 +184,7 @@ export function TMDataGridHeaderCell({
184
184
 
185
185
  /**
186
186
  * Sizes the column to its widest mounted content. The container is found
187
- * from the cell rather than passed down — the header always renders inside
187
+ * from the cell rather than passed down - the header always renders inside
188
188
  * the grid's scroll container, and that is the subtree autosize measures.
189
189
  */
190
190
  function autosize() {
@@ -197,7 +197,7 @@ export function TMDataGridHeaderCell({
197
197
  /**
198
198
  * Pinned columns are positioned with `getStart()` / `getAfter()`, which sum
199
199
  * `column.getSize()`. Fluid (`fr`) columns don't report their rendered width
200
- * there, so freeze the measured width into `columnSizing` as we pin — that
200
+ * there, so freeze the measured width into `columnSizing` as we pin - that
201
201
  * keeps the column looking the same and keeps the offsets exact.
202
202
  */
203
203
  function setPinned(position: "left" | "right" | false) {
@@ -265,7 +265,7 @@ export function TMDataGridHeaderCell({
265
265
  }
266
266
  if (isGroupingActive) {
267
267
  // Under the default `groupedColumnMode: "remove"` a grouped column is taken
268
- // out of the grid, so its own header — and the Ungroup item on it — is no
268
+ // out of the grid, so its own header (and the Ungroup item on it) is no
269
269
  // longer reachable. The tree column that replaced it carries them instead,
270
270
  // which is also where someone would look for them.
271
271
  if (column.id === GROUP_COLUMN_ID) {
@@ -284,7 +284,7 @@ export function TMDataGridHeaderCell({
284
284
  }
285
285
  }
286
286
  // Groups only, though `expanded` also holds which rows have their detail
287
- // panel open — `toggleAllRowsExpanded` would open every one of them from an
287
+ // panel open - `toggleAllRowsExpanded` would open every one of them from an
288
288
  // item that says "groups". See resolveExpandAll.
289
289
  const expandAllGroups = (expand: boolean) =>
290
290
  table.setExpanded(
@@ -349,7 +349,7 @@ export function TMDataGridHeaderCell({
349
349
  menuItems.push(<Menu.Divider key="pin-divider" />);
350
350
  }
351
351
  if (canReorder) {
352
- // The keyboard path to reordering, and the discoverable one — a header you
352
+ // The keyboard path to reordering, and the discoverable one - a header you
353
353
  // can drag looks no different from one you cannot.
354
354
  const previous = getStepTargetColumn({
355
355
  table,
@@ -421,12 +421,12 @@ export function TMDataGridHeaderCell({
421
421
  </Menu.Item>,
422
422
  );
423
423
  }
424
- // A trailing divider reads as a dropped item — drop the divider instead.
424
+ // A trailing divider reads as a dropped item - drop the divider instead.
425
425
  while (menuItems.length > 0 && isDivider(menuItems.at(-1))) {
426
426
  menuItems.pop();
427
427
  }
428
428
 
429
- // One gate for both ways into the menu — the button and the right-click —
429
+ // One gate for both ways into the menu - the button and the right-click -
430
430
  // so the two can never disagree about which headers have one. A control
431
431
  // lane's header is itself a control (select-all, expand-all), so it carries
432
432
  // no column menu, and a right-click there falls through to the browser's.
@@ -436,7 +436,7 @@ export function TMDataGridHeaderCell({
436
436
  classes.headerCell,
437
437
  canSort ? classes.headerCellSortable : "",
438
438
  // Only the filter tints the title. `data-active` below stays
439
- // sorted-or-filtered — it is the queryable state, not the styling hook.
439
+ // sorted-or-filtered - it is the queryable state, not the styling hook.
440
440
  isFiltered ? classes.headerCellFiltered : "",
441
441
  isDragged ? classes.headerCellDragging : "",
442
442
  dropSide === "before" ? classes.headerCellDropBefore : "",
@@ -615,7 +615,7 @@ export function TMDataGridHeaderCell({
615
615
  )}
616
616
 
617
617
  {/* A system lane has no separator at all: it cannot be resized, and the
618
- hover line reads as a handle that does nothing — the one place the
618
+ hover line reads as a handle that does nothing - the one place the
619
619
  separator stops being a divider and starts being a lie. */}
620
620
  {!isControlColumn(column.id) && (
621
621
  <div
@@ -659,7 +659,7 @@ export function TMDataGridHeaderCell({
659
659
  // A second Menu for the same items, rather than one Menu serving both ways
660
660
  // in: the button's dropdown hangs off the button, while a context menu
661
661
  // belongs at the pointer, and a Mantine Menu has one target. `menuItems` is
662
- // built once and rendered by whichever dropdown is open — never both — so
662
+ // built once and rendered by whichever dropdown is open (never both), so
663
663
  // the two can't drift apart.
664
664
  //
665
665
  // The per-header Popover this adds is not the cost the body pays for one per
@@ -4,14 +4,14 @@ import type { TMDataGridFeatures } from "../useTMDataGrid";
4
4
  export const ROW_NUMBER_COLUMN_ID = "__rowNumber__";
5
5
 
6
6
  /**
7
- * The generated row-number gutter, prepended under `enableRowNumbers` —
7
+ * The generated row-number gutter, prepended under `enableRowNumbers` -
8
8
  * outermost left, before even the checkbox lane, the way a spreadsheet keeps
9
9
  * its gutter outside everything.
10
10
  *
11
11
  * The def renders nothing itself: a cell cannot know its display position,
12
- * so the body computes the numbers — one pass over the view, group rows
12
+ * so the body computes the numbers - one pass over the view, group rows
13
13
  * skipped (they are headings over the rows being counted), continuing across
14
- * pages — and substitutes them in. See `TMDataGridTable`.
14
+ * pages - and substitutes them in. See `TMDataGridTable`.
15
15
  *
16
16
  * A system lane: fixed width, no menu, no resize, never exported, and its
17
17
  * visibility follows the option rather than the columns panel.
@@ -37,7 +37,7 @@ export function createRowNumberColumn<TData extends RowData>(
37
37
  enablePinning: false,
38
38
  enableHiding: false,
39
39
  header: () => "#",
40
- // The body substitutes the number; a row without one — a group row —
40
+ // The body substitutes the number; a row without one - a group row -
41
41
  // falls through to this.
42
42
  cell: () => null,
43
43
  };