@jielga/tmdatagrid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/index.d.ts +592 -0
- package/dist/index.js +1590 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +111 -0
- package/skills/columns/SKILL.md +195 -0
- package/skills/features/SKILL.md +214 -0
- package/skills/getting-started/SKILL.md +209 -0
- package/skills/options/SKILL.md +208 -0
- package/skills/server-side/SKILL.md +150 -0
- package/src/tmdatagrid/TMDataGrid.module.css +410 -0
- package/src/tmdatagrid/TMDataGrid.tsx +101 -0
- package/src/tmdatagrid/TMDataGridColumnsPanel.tsx +91 -0
- package/src/tmdatagrid/TMDataGridContext.ts +32 -0
- package/src/tmdatagrid/TMDataGridFilterPanel.tsx +182 -0
- package/src/tmdatagrid/TMDataGridFooter.tsx +74 -0
- package/src/tmdatagrid/TMDataGridHeaderCell.tsx +466 -0
- package/src/tmdatagrid/TMDataGridTable.tsx +297 -0
- package/src/tmdatagrid/TMDataGridToolbar.tsx +131 -0
- package/src/tmdatagrid/capabilities.ts +117 -0
- package/src/tmdatagrid/columnOrdering.ts +140 -0
- package/src/tmdatagrid/columnUtils.ts +43 -0
- package/src/tmdatagrid/filterOperators.ts +171 -0
- package/src/tmdatagrid/icons.ts +25 -0
- package/src/tmdatagrid/index.ts +68 -0
- package/src/tmdatagrid/persistence.ts +187 -0
- package/src/tmdatagrid/sizes.ts +29 -0
- package/src/tmdatagrid/useTMDataGrid.tsx +378 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: columns
|
|
3
|
+
description: >
|
|
4
|
+
Define TMDataGrid columns with createTMDataGridColumnHelper: column meta
|
|
5
|
+
(label, type, flex, align, enableOrdering), fluid minmax sizing versus fixed
|
|
6
|
+
width, minSize and maxSize, per-column
|
|
7
|
+
enableSorting/enableColumnFilter/enableHiding/enablePinning/ enableResizing,
|
|
8
|
+
the generated SELECT_COLUMN_ID checkbox column, and the shared filter function
|
|
9
|
+
with its operator list and isFilterActive. Load when adding or changing
|
|
10
|
+
columns, controlling widths, or wiring column filters.
|
|
11
|
+
metadata:
|
|
12
|
+
type: core
|
|
13
|
+
library: '@jielga/tmdatagrid'
|
|
14
|
+
library_version: '0.1.0'
|
|
15
|
+
sources:
|
|
16
|
+
- 'Jielga/TMDataGrid:src/docs/columns.md'
|
|
17
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/filterOperators.ts'
|
|
18
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/columnUtils.ts'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# TMDataGrid — Columns
|
|
22
|
+
|
|
23
|
+
`createTMDataGridColumnHelper<TData>()` returns a TanStack column helper bound to
|
|
24
|
+
the grid feature set, which gives `meta` and `filterFn` their correct types.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { createTMDataGridColumnHelper } from "@jielga/tmdatagrid";
|
|
28
|
+
|
|
29
|
+
const columnHelper = createTMDataGridColumnHelper<Employee>();
|
|
30
|
+
|
|
31
|
+
const columns = columnHelper.columns([
|
|
32
|
+
columnHelper.accessor("salary", {
|
|
33
|
+
header: "Salary",
|
|
34
|
+
minSize: 130,
|
|
35
|
+
meta: { type: "number", align: "right" },
|
|
36
|
+
cell: (info) => formatSek(info.getValue()),
|
|
37
|
+
}),
|
|
38
|
+
columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
39
|
+
id: "fullName",
|
|
40
|
+
header: "Full name",
|
|
41
|
+
meta: { label: "Full name" },
|
|
42
|
+
}),
|
|
43
|
+
]);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Define columns at module scope. A new array on every render rebuilds the table's
|
|
47
|
+
column model.
|
|
48
|
+
|
|
49
|
+
## meta
|
|
50
|
+
|
|
51
|
+
| Field | Type | Default | Description |
|
|
52
|
+
| --- | --- | --- | --- |
|
|
53
|
+
| `label` | `string` | String header, or column id | Name used in menus and the column manager. Required when `header` is a component. |
|
|
54
|
+
| `type` | `"string" \| "number"` | `"string"` | Determines which filter operators are offered. |
|
|
55
|
+
| `flex` | `number` | `1` | Share of the remaining width. |
|
|
56
|
+
| `align` | `"left" \| "right" \| "center"` | `"left"` | Applied to both header and cells. |
|
|
57
|
+
| `enableOrdering` | `boolean` | `true` | `false` keeps the column where it is. |
|
|
58
|
+
|
|
59
|
+
`enableOrdering` lives in `meta` because column ordering is the one feature
|
|
60
|
+
TanStack defines no column option for. See the `features` skill.
|
|
61
|
+
|
|
62
|
+
## Sizing
|
|
63
|
+
|
|
64
|
+
Columns are fluid. Each track is `minmax(minSize, flex fr)`.
|
|
65
|
+
|
|
66
|
+
| Option | Effect |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `minSize` | Minimum width, and the column's contribution to the grid minimum width. Defaults to `80`. |
|
|
69
|
+
| `meta.flex` | Share of the remaining width. |
|
|
70
|
+
| `minSize === maxSize` | Fixed width. The column is never fluid. |
|
|
71
|
+
| `size` | Applied once the column becomes fixed by resizing or pinning. |
|
|
72
|
+
|
|
73
|
+
A column switches to a fixed pixel width when resized or pinned. Pinning requires
|
|
74
|
+
it because sticky offsets are calculated from `getSize()`, which cannot resolve
|
|
75
|
+
an `fr` value. The grid stores the column's rendered width in `columnSizing` at
|
|
76
|
+
the moment it is pinned, so its width does not change.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Fixed 64px action column that never flexes.
|
|
80
|
+
columnHelper.display({
|
|
81
|
+
id: "actions",
|
|
82
|
+
minSize: 64,
|
|
83
|
+
maxSize: 64,
|
|
84
|
+
cell: (info) => <RowMenu id={info.row.original.id} />,
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Column-level feature options
|
|
89
|
+
|
|
90
|
+
Standard TanStack column options. Each also removes the corresponding interface.
|
|
91
|
+
|
|
92
|
+
| Option | Effect when `false` |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `enableSorting` | No sort indicator and no sort menu items. |
|
|
95
|
+
| `enableColumnFilter` | No filter menu item. Excluded from the filter panel column list. |
|
|
96
|
+
| `enableHiding` | No hide menu item. Checkbox disabled in the column manager. |
|
|
97
|
+
| `enablePinning` | No pin menu items. |
|
|
98
|
+
| `enableResizing` | The divider is displayed but cannot be dragged. |
|
|
99
|
+
|
|
100
|
+
`meta.enableOrdering: false` belongs to the same set and removes the column's
|
|
101
|
+
header dragging and move menu items. A column inside a header group is never
|
|
102
|
+
movable whatever it says, because `columnOrder` sequences leaf columns.
|
|
103
|
+
|
|
104
|
+
A column whose menu has no remaining items renders no menu button.
|
|
105
|
+
|
|
106
|
+
## Checkbox column
|
|
107
|
+
|
|
108
|
+
Unless `enableRowSelection` is `false`, a column with the id `SELECT_COLUMN_ID`
|
|
109
|
+
is added as the first column and pinned to the left. It is listed as "Checkbox
|
|
110
|
+
selection" in the column manager and can be hidden like any other column. It has
|
|
111
|
+
no column menu and cannot be sorted, filtered, resized, re-pinned or moved.
|
|
112
|
+
|
|
113
|
+
Because it cannot be moved, it also anchors the left pinned region: no column
|
|
114
|
+
can be placed in front of it.
|
|
115
|
+
|
|
116
|
+
## Filtering
|
|
117
|
+
|
|
118
|
+
All columns share one filter function. The operator lives in the filter value
|
|
119
|
+
rather than being selected through `filterFn`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
type TMDataGridFilterValue = {
|
|
123
|
+
operator: TMDataGridFilterOperator;
|
|
124
|
+
value: string;
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The filter model is therefore plain JSON and can be forwarded to a server without
|
|
129
|
+
transformation. See the `server-side` skill.
|
|
130
|
+
|
|
131
|
+
### Operators
|
|
132
|
+
|
|
133
|
+
| Operator | Label | Column type |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `contains` | contains | `string` |
|
|
136
|
+
| `equals` | equals | `string`, `number` |
|
|
137
|
+
| `notEquals` | does not equal | `string`, `number` |
|
|
138
|
+
| `startsWith` | starts with | `string` |
|
|
139
|
+
| `endsWith` | ends with | `string` |
|
|
140
|
+
| `greaterThan` | is greater than | `number` |
|
|
141
|
+
| `greaterThanOrEqual` | is greater than or equal to | `number` |
|
|
142
|
+
| `lessThan` | is less than | `number` |
|
|
143
|
+
| `lessThanOrEqual` | is less than or equal to | `number` |
|
|
144
|
+
| `isEmpty` | is empty | `string`, `number` |
|
|
145
|
+
| `isNotEmpty` | is not empty | `string`, `number` |
|
|
146
|
+
|
|
147
|
+
`meta.type` selects the list. String comparisons are case-insensitive.
|
|
148
|
+
`FILTER_OPERATOR_LABELS` maps operators to their labels.
|
|
149
|
+
|
|
150
|
+
To give a column its own matching logic, set `filterFn` on the column definition.
|
|
151
|
+
The grid only provides `"tmDataGrid"` as the default.
|
|
152
|
+
|
|
153
|
+
## Common mistakes
|
|
154
|
+
|
|
155
|
+
### Component header without meta.label
|
|
156
|
+
|
|
157
|
+
`getColumnLabel(column)` falls back to a *string* header, then the column id.
|
|
158
|
+
When `header` is a component there is no string to fall back to, so the column
|
|
159
|
+
manager and column menus show the raw id (`"fullName"`) instead of a name.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// Wrong — menus show "fullName".
|
|
163
|
+
columnHelper.accessor("fullName", { header: () => <Icon /> });
|
|
164
|
+
|
|
165
|
+
// Right.
|
|
166
|
+
columnHelper.accessor("fullName", {
|
|
167
|
+
header: () => <Icon />,
|
|
168
|
+
meta: { label: "Full name" },
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Treating an empty filter as no filter
|
|
173
|
+
|
|
174
|
+
A filter with an empty value stays in state so the panel keeps showing its row
|
|
175
|
+
while the user types. It matches all rows and does not light up the header
|
|
176
|
+
indicator. Test with `isFilterActive(value)` rather than checking for presence:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { isFilterActive } from "@jielga/tmdatagrid";
|
|
180
|
+
|
|
181
|
+
const active = columnFilters.filter((filter) => isFilterActive(filter.value));
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Numeric operators on a column without meta.type
|
|
185
|
+
|
|
186
|
+
`getColumnType(column)` defaults to `"string"`, so a numeric column that omits
|
|
187
|
+
`meta: { type: "number" }` offers only the string operators — `greaterThan` and
|
|
188
|
+
the other comparisons never appear in the panel, and comparisons run as text.
|
|
189
|
+
|
|
190
|
+
### Setting size to control width
|
|
191
|
+
|
|
192
|
+
`size` is only applied once the column becomes fixed by resizing or pinning.
|
|
193
|
+
On a fluid column it is ignored; the track is `minmax(minSize, flex fr)`. Use
|
|
194
|
+
`minSize` for a floor and `meta.flex` for the share, or `minSize === maxSize`
|
|
195
|
+
for a genuinely fixed column.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: features
|
|
3
|
+
description: >
|
|
4
|
+
Enable or disable TMDataGrid interface elements through standard TanStack
|
|
5
|
+
table and column options, and reuse the same checks in your own components.
|
|
6
|
+
Covers the enableSorting/enableColumnFilters/enableHiding/enableColumnPinning/
|
|
7
|
+
enableColumnResizing/enableColumnOrdering/enableRowSelection matrix,
|
|
8
|
+
getGridCapabilities and getColumnCapabilities, why the features argument is
|
|
9
|
+
required for reactivity under the React Compiler, column pinning, column
|
|
10
|
+
ordering with moveColumn and moveColumnByStep, and always-on virtualization.
|
|
11
|
+
Load when building a read-only grid, hiding grid chrome, reordering columns
|
|
12
|
+
from code, or writing a custom toolbar button.
|
|
13
|
+
metadata:
|
|
14
|
+
type: core
|
|
15
|
+
library: '@jielga/tmdatagrid'
|
|
16
|
+
library_version: '0.1.0'
|
|
17
|
+
sources:
|
|
18
|
+
- 'Jielga/TMDataGrid:src/docs/features.md'
|
|
19
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/capabilities.ts'
|
|
20
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/columnOrdering.ts'
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# TMDataGrid — Features and capabilities
|
|
24
|
+
|
|
25
|
+
Almost every control is bound to the TanStack capability check for that feature,
|
|
26
|
+
so setting the standard table or column option removes the corresponding
|
|
27
|
+
interface. Empty menus and inactive buttons are never rendered.
|
|
28
|
+
|
|
29
|
+
Column ordering is the one exception. TanStack's `columnOrderingFeature` ships
|
|
30
|
+
state and APIs but no `enable` option, so the grid defines
|
|
31
|
+
`enableColumnOrdering` and `meta.enableOrdering` itself. They behave like the
|
|
32
|
+
options around them.
|
|
33
|
+
|
|
34
|
+
## Option reference
|
|
35
|
+
|
|
36
|
+
| Option | Level | Interface removed |
|
|
37
|
+
| --- | --- | --- |
|
|
38
|
+
| `enableSorting: false` | Table, column | Sort indicator, sort menu items, click-to-sort |
|
|
39
|
+
| `enableColumnFilters: false` | Table | Filter menu item, `FilterButton`, filter panel |
|
|
40
|
+
| `enableColumnFilter: false` | Column | That column's filter menu item and panel entry |
|
|
41
|
+
| `enableHiding: false` | Table, column | Hide column, Manage columns, `ColumnsButton` |
|
|
42
|
+
| `enableColumnPinning: false` | Table | Pin and unpin menu items |
|
|
43
|
+
| `enablePinning: false` | Column | That column's pin menu items |
|
|
44
|
+
| `enableColumnResizing: false` | Table | Resize dragging. The divider remains as a separator |
|
|
45
|
+
| `enableResizing: false` | Column | That column's resize dragging |
|
|
46
|
+
| `enableColumnOrdering: false` | Table | Header dragging and the move menu items |
|
|
47
|
+
| `meta.enableOrdering: false` | Column | That column's header dragging and move menu items |
|
|
48
|
+
| `enableRowSelection: false` | Table | The checkbox column |
|
|
49
|
+
|
|
50
|
+
A column whose menu has no remaining items renders no menu button. Dividers are
|
|
51
|
+
never left at the end of a menu.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// Read-only grid: no selection, hiding, pinning or filtering.
|
|
55
|
+
const grid = useTMDataGrid({
|
|
56
|
+
data,
|
|
57
|
+
columns,
|
|
58
|
+
enableRowSelection: false,
|
|
59
|
+
enableHiding: false,
|
|
60
|
+
enableColumnPinning: false,
|
|
61
|
+
enableColumnFilters: false,
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Pagination is not controlled by an option — omit `TMDataGrid.Footer` and no
|
|
66
|
+
pagination controls are rendered.
|
|
67
|
+
|
|
68
|
+
## Capability helpers
|
|
69
|
+
|
|
70
|
+
Use these to apply the same checks in your own components.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { getGridCapabilities, useTMDataGridContext } from "@jielga/tmdatagrid";
|
|
74
|
+
|
|
75
|
+
function ExportButton() {
|
|
76
|
+
const { table, features } = useTMDataGridContext();
|
|
77
|
+
const { canFilterAny } = getGridCapabilities(table, features);
|
|
78
|
+
|
|
79
|
+
if (!canFilterAny) return null;
|
|
80
|
+
return <Button onClick={exportRows}>Export</Button>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`getGridCapabilities(table, features)`:
|
|
85
|
+
|
|
86
|
+
| Field | Description |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `canSortAny` | At least one leaf column can be sorted. |
|
|
89
|
+
| `canFilterAny` | At least one leaf column can be filtered. |
|
|
90
|
+
| `canHideAny` | At least one leaf column can be hidden. |
|
|
91
|
+
| `canPinAny` | At least one leaf column can be pinned. |
|
|
92
|
+
| `canReorderAny` | At least one leaf column can be moved. |
|
|
93
|
+
| `canSelectRows` | `enableRowSelection` is not `false`. |
|
|
94
|
+
|
|
95
|
+
`getColumnCapabilities(column, features)` returns the same information for a
|
|
96
|
+
single column as `canSort`, `canFilter`, `canHide`, `canPin`, `canResize` and
|
|
97
|
+
`canReorder`.
|
|
98
|
+
|
|
99
|
+
`readFeatureFlags(options)` derives the flags from a table options object.
|
|
100
|
+
|
|
101
|
+
## Why capabilities take a features argument
|
|
102
|
+
|
|
103
|
+
`features` is returned by `useTMDataGrid` and re-derived from the options object
|
|
104
|
+
on every render. It is required *in addition to* TanStack's `getCanX()` methods
|
|
105
|
+
because it is what makes the result reactive.
|
|
106
|
+
|
|
107
|
+
`column.getCanSort()` is a method call on a column object whose identity is
|
|
108
|
+
preserved across an options change. Under the React Compiler the call is
|
|
109
|
+
memoized, so a grid whose `enableSorting` flips to `false` would keep rendering
|
|
110
|
+
sort indicators. Passing `features` supplies a value that actually changes, while
|
|
111
|
+
`getCanX()` still determines the outcome and applies per-column overrides.
|
|
112
|
+
|
|
113
|
+
The same rule applies to any value derived from the table elsewhere in an
|
|
114
|
+
application: read state through `useSelector(table.store, …)` and options through
|
|
115
|
+
`features`, rather than calling methods on a long-lived object.
|
|
116
|
+
|
|
117
|
+
## Column pinning
|
|
118
|
+
|
|
119
|
+
Pin to left and Pin to right appear in each column menu. The current position is
|
|
120
|
+
marked, and selecting it again unpins. Pinned columns are sticky within the
|
|
121
|
+
scroll container, and the boundary is marked with a divider and a short gradient.
|
|
122
|
+
|
|
123
|
+
Headers, cells and grid tracks are ordered left, centre, right from the same
|
|
124
|
+
source, so pinning does not change a column's position relative to its group.
|
|
125
|
+
|
|
126
|
+
Pinning is stored in `columnPinning`, one of the slices covered by `settingsKey`
|
|
127
|
+
in the `options` skill.
|
|
128
|
+
|
|
129
|
+
## Column ordering
|
|
130
|
+
|
|
131
|
+
Enabled by default. Drag a column header sideways to move it; "Move left" and
|
|
132
|
+
"Move right" in the column menu do the same one step at a time, and are the path
|
|
133
|
+
that works without a pointer.
|
|
134
|
+
|
|
135
|
+
A column only moves within its own pinned region, so a header in another region
|
|
136
|
+
never accepts the drop. That follows TanStack's pipeline: pinning splits the
|
|
137
|
+
grid into left, centre and right, then `columnOrder` sequences the centre while
|
|
138
|
+
`columnPinning.left` / `.right` sequence the pinned lanes. Unpin first to move a
|
|
139
|
+
column out of a pinned region.
|
|
140
|
+
|
|
141
|
+
Ordering writes `columnOrder` as the complete leaf order — hidden and pinned
|
|
142
|
+
columns included — so a column keeps its position when it is later shown or
|
|
143
|
+
unpinned; moving a pinned column also rewrites its `columnPinning` array. Both
|
|
144
|
+
slices are covered by `settingsKey`.
|
|
145
|
+
|
|
146
|
+
A neighbour that cannot be moved acts as a wall rather than being stepped over,
|
|
147
|
+
which is what keeps the checkbox column at the start of the left region.
|
|
148
|
+
Columns inside a header group are never movable: `columnOrder` sequences leaf
|
|
149
|
+
columns, so moving one would leave the group header spanning columns that no
|
|
150
|
+
longer belong to it.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import {
|
|
154
|
+
getStepTargetColumn,
|
|
155
|
+
moveColumn,
|
|
156
|
+
moveColumnByStep,
|
|
157
|
+
} from "@jielga/tmdatagrid";
|
|
158
|
+
|
|
159
|
+
moveColumn({ table, columnId: "salary", targetId: "age", side: "before" });
|
|
160
|
+
moveColumnByStep({ table, columnId: "salary", direction: 1 });
|
|
161
|
+
|
|
162
|
+
// null at the edge of a region — what the move menu items disable themselves on.
|
|
163
|
+
const next = getStepTargetColumn({ table, columnId: "salary", direction: 1 });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Both movers are no-ops for a move that is not allowed, including one across
|
|
167
|
+
regions.
|
|
168
|
+
|
|
169
|
+
## Virtualization
|
|
170
|
+
|
|
171
|
+
Always enabled and not configurable. Only rows within the viewport, plus a small
|
|
172
|
+
overscan, are mounted, so page size does not affect how much is rendered.
|
|
173
|
+
|
|
174
|
+
Row height comes from `meta.rowHeight`, or from the `size` prop when unset. Rows
|
|
175
|
+
are fixed height; an accurate value keeps scrolling precise.
|
|
176
|
+
|
|
177
|
+
## Common mistakes
|
|
178
|
+
|
|
179
|
+
### Calling getCanX() in a custom component
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// Wrong — memoized under the React Compiler, so the button
|
|
183
|
+
// never reappears when enableColumnFilters flips back to true.
|
|
184
|
+
const { table } = useTMDataGridContext();
|
|
185
|
+
if (!table.getAllLeafColumns().some((c) => c.getCanFilter())) return null;
|
|
186
|
+
|
|
187
|
+
// Right — features is a value that changes.
|
|
188
|
+
const { table, features } = useTMDataGridContext();
|
|
189
|
+
const { canFilterAny } = getGridCapabilities(table, features);
|
|
190
|
+
if (!canFilterAny) return null;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Setting a custom row height in CSS only
|
|
194
|
+
|
|
195
|
+
The virtualizer needs the height as a number to position rows. Overriding row
|
|
196
|
+
height in a stylesheet leaves the virtualizer measuring the old value, so rows
|
|
197
|
+
overlap or leave gaps as you scroll. Set `meta.rowHeight` instead.
|
|
198
|
+
|
|
199
|
+
### Reordering a pinned column through columnOrder
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// Wrong — the left and right regions are sequenced by columnPinning,
|
|
203
|
+
// so this moves nothing on screen.
|
|
204
|
+
table.setColumnOrder(["__select__", "department", "id", …]);
|
|
205
|
+
|
|
206
|
+
// Right — moveColumn writes whichever slice the column's region uses.
|
|
207
|
+
moveColumn({ table, columnId: "department", targetId: "id", side: "before" });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Expecting a table-level flag to override a column
|
|
211
|
+
|
|
212
|
+
Table and column options compose — the table flag gates the feature, and the
|
|
213
|
+
column flag narrows it further. `enableSorting: true` on a column does not
|
|
214
|
+
re-enable sorting for a table that set `enableSorting: false`.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: getting-started
|
|
3
|
+
description: >
|
|
4
|
+
Set up TMDataGrid, a compound React data grid built on TanStack Table v9 and
|
|
5
|
+
Mantine. Covers useTMDataGrid, the TMDataGrid root, context, the component
|
|
6
|
+
catalog (Table, Footer, Toolbar, Spacer, SummaryCount, FilterButton,
|
|
7
|
+
ColumnsButton, FilterPanel, ColumnsPanel), the size scale and the
|
|
8
|
+
bounded-height layout requirement. Load when adding a grid, choosing which
|
|
9
|
+
parts to render, or when rows do not appear.
|
|
10
|
+
metadata:
|
|
11
|
+
type: core
|
|
12
|
+
library: '@jielga/tmdatagrid'
|
|
13
|
+
library_version: '0.1.0'
|
|
14
|
+
sources:
|
|
15
|
+
- 'Jielga/TMDataGrid:src/docs/getting-started.md'
|
|
16
|
+
- 'Jielga/TMDataGrid:src/docs/components.md'
|
|
17
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/TMDataGrid.tsx'
|
|
18
|
+
- 'Jielga/TMDataGrid:src/tmdatagrid/sizes.ts'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# TMDataGrid — Getting started
|
|
22
|
+
|
|
23
|
+
`useTMDataGrid` creates the table, `TMDataGrid` provides it through context, and
|
|
24
|
+
the parts rendered inside read what they need from that context. Only the parts
|
|
25
|
+
you render exist: leave out `TMDataGrid.Footer` and there is no pagination.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install @jielga/tmdatagrid
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Peer dependencies: `react` and `react-dom` (19.1+), `@mantine/core`,
|
|
34
|
+
`@tanstack/react-table` (v9), `@tanstack/react-store`, `@tanstack/store`,
|
|
35
|
+
`@tanstack/react-virtual`, `@tabler/icons-react`.
|
|
36
|
+
|
|
37
|
+
Import both stylesheets once, Mantine's first, and render inside a
|
|
38
|
+
`MantineProvider`:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import "@mantine/core/styles.css";
|
|
42
|
+
import "@jielga/tmdatagrid/styles.css";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Setup
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import {
|
|
49
|
+
createTMDataGridColumnHelper,
|
|
50
|
+
TMDataGrid,
|
|
51
|
+
useTMDataGrid,
|
|
52
|
+
} from "@jielga/tmdatagrid";
|
|
53
|
+
|
|
54
|
+
type Employee = { id: number; firstName: string; age: number };
|
|
55
|
+
|
|
56
|
+
const columnHelper = createTMDataGridColumnHelper<Employee>();
|
|
57
|
+
|
|
58
|
+
const columns = columnHelper.columns([
|
|
59
|
+
columnHelper.accessor("id", { header: "ID", meta: { type: "number" } }),
|
|
60
|
+
columnHelper.accessor("firstName", { header: "First name" }),
|
|
61
|
+
columnHelper.accessor("age", {
|
|
62
|
+
header: "Age",
|
|
63
|
+
meta: { type: "number", align: "right" },
|
|
64
|
+
}),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
export function Employees({ data }: { data: Employee[] }) {
|
|
68
|
+
const grid = useTMDataGrid({
|
|
69
|
+
data,
|
|
70
|
+
columns,
|
|
71
|
+
getRowId: (row) => String(row.id),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<TMDataGrid {...grid} size="md" style={{ flex: 1, minHeight: 0 }}>
|
|
76
|
+
<TMDataGrid.Toolbar>
|
|
77
|
+
<TMDataGrid.SummaryCount />
|
|
78
|
+
<TMDataGrid.Spacer />
|
|
79
|
+
<TMDataGrid.FilterButton />
|
|
80
|
+
<TMDataGrid.ColumnsButton />
|
|
81
|
+
</TMDataGrid.Toolbar>
|
|
82
|
+
|
|
83
|
+
<TMDataGrid.Table<Employee> />
|
|
84
|
+
<TMDataGrid.Footer />
|
|
85
|
+
</TMDataGrid>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Spread the hook result (`{...grid}`) rather than assigning `table`, `ui` and
|
|
91
|
+
`features` individually.
|
|
92
|
+
|
|
93
|
+
## Defaults
|
|
94
|
+
|
|
95
|
+
| Behaviour | Notes |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| Virtualized rows | Always enabled, not configurable. Only rows in view are mounted. |
|
|
98
|
+
| Resizable columns | Drag the divider on a header's trailing edge. |
|
|
99
|
+
| Reorderable columns | Drag a header sideways, or use the column menu. |
|
|
100
|
+
| Sorting | Click a header, or use the column menu. |
|
|
101
|
+
| Column menu | On hover: sort, filter, pin, move, hide, manage columns. |
|
|
102
|
+
| Filter panel | Column, operator and value rows. |
|
|
103
|
+
| Column manager | Search, toggle, show/hide all, reset. |
|
|
104
|
+
| Row selection | Checkbox column pinned to the left. |
|
|
105
|
+
| Pagination | Only when `TMDataGrid.Footer` is rendered. |
|
|
106
|
+
| Sizing | `size="xs"` to `size="xl"` scales rows, type and controls. |
|
|
107
|
+
|
|
108
|
+
Each is bound to a capability check — disabling the standard table or column
|
|
109
|
+
option also removes its interface. Column ordering is the one switch the grid
|
|
110
|
+
defines itself, as `enableColumnOrdering`. See the `features` skill.
|
|
111
|
+
|
|
112
|
+
## Layout
|
|
113
|
+
|
|
114
|
+
`TMDataGrid` renders a flex column with `overflow: hidden`. It needs a bounded
|
|
115
|
+
height, either `style={{ flex: 1, minHeight: 0 }}` inside a flex parent or a
|
|
116
|
+
fixed height. The table area then fills the remaining space.
|
|
117
|
+
|
|
118
|
+
Columns are fluid by default: each track is `minmax(minSize, flex fr)`. A column
|
|
119
|
+
takes a fixed pixel width once resized or pinned.
|
|
120
|
+
|
|
121
|
+
## Components
|
|
122
|
+
|
|
123
|
+
All read the grid from context and must be rendered inside `TMDataGrid`. Order
|
|
124
|
+
and presence are up to you — the root is a plain flex column.
|
|
125
|
+
|
|
126
|
+
| Component | Props | Notes |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `TMDataGrid` | `table`, `ui`, `features`, `size`, `className`, `style` | Root. Provides context. |
|
|
129
|
+
| `TMDataGrid.Table` | `onRowClick(row)` | Header, virtualized body, filter panel. |
|
|
130
|
+
| `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]`) | Pagination controls. |
|
|
131
|
+
| `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
|
|
132
|
+
| `TMDataGrid.Spacer` | — | Pushes later toolbar items right. |
|
|
133
|
+
| `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
|
|
134
|
+
| `TMDataGrid.FilterButton` | — | Toggles filter panel. Renders nothing if no column is filterable. |
|
|
135
|
+
| `TMDataGrid.ColumnsButton` | — | Opens column manager. Renders nothing if no column is hideable. |
|
|
136
|
+
| `TMDataGrid.FilterPanel` | — | Rendered by `.Table`; exported for custom layouts. |
|
|
137
|
+
| `TMDataGrid.ColumnsPanel` | — | Rendered by `.ColumnsButton`; exported for custom layouts. |
|
|
138
|
+
|
|
139
|
+
Pass the row type so `onRowClick` stays typed:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`TMDataGrid.Footer` reads totals from `table.getRowCount()`, which prefers
|
|
146
|
+
`options.rowCount`. `TMDataGrid.SummaryCount` uses `meta.totalRowCount` when
|
|
147
|
+
provided, otherwise the pre-filtered row count.
|
|
148
|
+
|
|
149
|
+
## size
|
|
150
|
+
|
|
151
|
+
Standard Mantine scale. Controls row height, header height, font size and cell
|
|
152
|
+
padding, and selects the size of the Mantine controls the grid renders.
|
|
153
|
+
|
|
154
|
+
| `size` | Row height | Header height | Font size | Cell padding |
|
|
155
|
+
| --- | --- | --- | --- | --- |
|
|
156
|
+
| `xs` | 34px | 32px | `xs` | 6px |
|
|
157
|
+
| `sm` | 42px | 38px | `sm` | 8px |
|
|
158
|
+
| `md` | 52px | 44px | `sm` | 10px |
|
|
159
|
+
| `lg` | 62px | 52px | `md` | 14px |
|
|
160
|
+
| `xl` | 72px | 60px | `lg` | 18px |
|
|
161
|
+
|
|
162
|
+
The virtualizer needs row height as a number, so it cannot come from CSS alone.
|
|
163
|
+
`SIZE_ROW_HEIGHT` is the exported source of these values and the stylesheet
|
|
164
|
+
mirrors them. Set `meta.rowHeight` for a height outside the scale.
|
|
165
|
+
|
|
166
|
+
## Helpers
|
|
167
|
+
|
|
168
|
+
| Export | Description |
|
|
169
|
+
| --- | --- |
|
|
170
|
+
| `useTMDataGridContext()` | The enclosing grid context. Throws outside `TMDataGrid`. |
|
|
171
|
+
| `openColumnFilter(api, columnId)` | Adds an empty filter for a column and opens the panel. |
|
|
172
|
+
| `getColumnLabel(column)` | `meta.label`, a string header, or the column id. |
|
|
173
|
+
| `getColumnType(column)` | `meta.type`, defaulting to `"string"`. |
|
|
174
|
+
| `moveColumn({ table, columnId, targetId, side })` | Moves a column next to another. See the `features` skill. |
|
|
175
|
+
| `moveColumnByStep({ table, columnId, direction })` | Moves a column one position within its region. |
|
|
176
|
+
| `SELECT_COLUMN_ID` | Id of the generated checkbox column. |
|
|
177
|
+
|
|
178
|
+
## Common mistakes
|
|
179
|
+
|
|
180
|
+
### Columns or data defined during render
|
|
181
|
+
|
|
182
|
+
A new array on every render rebuilds the table's column model. Define `columns`
|
|
183
|
+
at module scope; keep `data` stable with `useMemo`.
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
// Wrong — rebuilds the column model every render.
|
|
187
|
+
export function Employees({ data }: { data: Employee[] }) {
|
|
188
|
+
const columns = columnHelper.columns([...]);
|
|
189
|
+
const grid = useTMDataGrid({ data, columns });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Root with no bounded height
|
|
194
|
+
|
|
195
|
+
The root is `overflow: hidden`. Without `flex: 1; minHeight: 0` in a flex parent
|
|
196
|
+
or an explicit height, the table area collapses and no rows are visible — there
|
|
197
|
+
is no error.
|
|
198
|
+
|
|
199
|
+
### Assigning the hook result field by field
|
|
200
|
+
|
|
201
|
+
`<TMDataGrid table={grid.table}>` omits `ui` and `features`. Panels stop opening
|
|
202
|
+
and feature toggles stop being reactive. Spread instead: `<TMDataGrid {...grid}>`.
|
|
203
|
+
|
|
204
|
+
### Expecting Footer to control pagination
|
|
205
|
+
|
|
206
|
+
Pagination is not an option. Omitting `TMDataGrid.Footer` removes the controls
|
|
207
|
+
but the table still paginates at `pageSize: 25` — rows beyond the first page
|
|
208
|
+
simply become unreachable. Set `initialState.pagination.pageSize` if you want
|
|
209
|
+
every row on one page.
|