react-glide-table 1.1.9 → 1.2.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/README.md +111 -47
- package/dist/compound.cjs +215 -15
- package/dist/compound.d.cts +3 -3
- package/dist/compound.d.ts +3 -3
- package/dist/compound.js +215 -15
- package/dist/core.cjs +140 -4
- package/dist/core.d.cts +20 -5
- package/dist/core.d.ts +20 -5
- package/dist/core.js +136 -4
- package/dist/index.cjs +223 -15
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +219 -15
- package/dist/{types-xxzMLUwM.d.cts → types-D031_07N.d.cts} +85 -3
- package/dist/{types-xxzMLUwM.d.ts → types-D031_07N.d.ts} +85 -3
- package/package.json +23 -1
package/README.md
CHANGED
|
@@ -18,24 +18,24 @@ Peer dependencies: `react` and `react-dom` (`^18` or `^19`).
|
|
|
18
18
|
|
|
19
19
|
The package is marked `"sideEffects": false` for tree-shaking. Prefer subpath imports when you only need one surface:
|
|
20
20
|
|
|
21
|
-
| Import
|
|
22
|
-
|
|
|
23
|
-
| `react-glide-table`
|
|
24
|
-
| `react-glide-table/compound` | `createTable` / `Table` / `DataTable` + related types
|
|
25
|
-
| `react-glide-table/core`
|
|
21
|
+
| Import | Contents |
|
|
22
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
23
|
+
| `react-glide-table` | Full barrel (compound + core) |
|
|
24
|
+
| `react-glide-table/compound` | `createTable` / `Table` / `DataTable` + related types |
|
|
25
|
+
| `react-glide-table/core` | `useGlideTable` + feature hooks/helpers (no compound UI) |
|
|
26
26
|
|
|
27
27
|
## Quick start (compound)
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
|
-
import { createTable } from "react-glide-table/compound"
|
|
31
|
-
import { useState } from "react"
|
|
30
|
+
import { createTable } from "react-glide-table/compound";
|
|
31
|
+
import { useState } from "react";
|
|
32
32
|
|
|
33
|
-
type Product = { id: string; name: string; qty: number }
|
|
33
|
+
type Product = { id: string; name: string; qty: number };
|
|
34
34
|
|
|
35
|
-
const ProductTable = createTable<Product>()
|
|
35
|
+
const ProductTable = createTable<Product>();
|
|
36
36
|
|
|
37
37
|
export function Products({ data }: { data: Product[] }) {
|
|
38
|
-
const [page, setPage] = useState(1)
|
|
38
|
+
const [page, setPage] = useState(1);
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<ProductTable
|
|
@@ -59,26 +59,26 @@ export function Products({ data }: { data: Product[] }) {
|
|
|
59
59
|
</ProductTable.Header>
|
|
60
60
|
<ProductTable.Pagination page={page} pageSize={10} onChange={setPage} />
|
|
61
61
|
</ProductTable>
|
|
62
|
-
)
|
|
62
|
+
);
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### Customization surface
|
|
67
67
|
|
|
68
|
-
| Slot / prop
|
|
69
|
-
|
|
|
70
|
-
| `classNames`
|
|
71
|
-
| `slots.Toolbar`
|
|
72
|
-
| `slots.Row`
|
|
73
|
-
| `slots.Pending` / `slots.Empty`
|
|
74
|
-
| `className` / column `className` / `headerClassName` | Extra class hooks
|
|
75
|
-
| `labels` / `summary` / `toolbar`
|
|
76
|
-
| `Column.render`
|
|
68
|
+
| Slot / prop | Role |
|
|
69
|
+
| ---------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
70
|
+
| `classNames` | Per-part Tailwind/utility classes (`root`, `scroll`, `row`, `cell`, `toolbar`, …) |
|
|
71
|
+
| `slots.Toolbar` | Top summary / actions region |
|
|
72
|
+
| `slots.Row` | Full row replacement (cells, selection, edit UI) |
|
|
73
|
+
| `slots.Pending` / `slots.Empty` | Loading and empty states |
|
|
74
|
+
| `className` / column `className` / `headerClassName` | Extra class hooks |
|
|
75
|
+
| `labels` / `summary` / `toolbar` | Copy and slot nodes |
|
|
76
|
+
| `Column.render` | Cell content custom render |
|
|
77
77
|
|
|
78
78
|
Row/cell **state** is exposed as `data-*` attributes for Tailwind variants:
|
|
79
79
|
|
|
80
80
|
- row: `data-selected`, `data-hovered`, `data-expandable`, `data-expanded`
|
|
81
|
-
- cell: `data-merged`, `data-selection-fill`, `data-editable`, `data-editing`, …
|
|
81
|
+
- cell: `data-merged`, `data-selection-fill`, `data-editable`, `data-editing`, `data-frozen`, …
|
|
82
82
|
|
|
83
83
|
Example: `row: "data-[selected]:bg-blue-600"`.
|
|
84
84
|
|
|
@@ -87,16 +87,16 @@ Row-level UI → `slots.Row`. Cell content → `Column.render`. Header/Cell are
|
|
|
87
87
|
## Escape hatch (`useGlideTable`)
|
|
88
88
|
|
|
89
89
|
```tsx
|
|
90
|
-
import { flexRender } from "@tanstack/react-table"
|
|
91
|
-
import { useGlideTable } from "react-glide-table/core"
|
|
92
|
-
import type { ColumnDef } from "react-glide-table/core"
|
|
90
|
+
import { flexRender } from "@tanstack/react-table";
|
|
91
|
+
import { useGlideTable } from "react-glide-table/core";
|
|
92
|
+
import type { ColumnDef } from "react-glide-table/core";
|
|
93
93
|
|
|
94
|
-
type Product = { id: string; name: string; qty: number }
|
|
94
|
+
type Product = { id: string; name: string; qty: number };
|
|
95
95
|
|
|
96
96
|
const columns: ColumnDef<Product, unknown>[] = [
|
|
97
97
|
{ accessorKey: "name", header: "Name" },
|
|
98
98
|
{ accessorKey: "qty", header: "Qty" },
|
|
99
|
-
]
|
|
99
|
+
];
|
|
100
100
|
|
|
101
101
|
export function ProductTable({ data }: { data: Product[] }) {
|
|
102
102
|
const { table, rows, scrollRef } = useGlideTable({
|
|
@@ -104,7 +104,7 @@ export function ProductTable({ data }: { data: Product[] }) {
|
|
|
104
104
|
columns,
|
|
105
105
|
getRowId: (row) => row.id,
|
|
106
106
|
rowSelectionMode: "multi",
|
|
107
|
-
})
|
|
107
|
+
});
|
|
108
108
|
|
|
109
109
|
return (
|
|
110
110
|
<div ref={scrollRef}>
|
|
@@ -116,7 +116,10 @@ export function ProductTable({ data }: { data: Product[] }) {
|
|
|
116
116
|
<th key={header.id}>
|
|
117
117
|
{header.isPlaceholder
|
|
118
118
|
? null
|
|
119
|
-
: flexRender(
|
|
119
|
+
: flexRender(
|
|
120
|
+
header.column.columnDef.header,
|
|
121
|
+
header.getContext(),
|
|
122
|
+
)}
|
|
120
123
|
</th>
|
|
121
124
|
))}
|
|
122
125
|
</tr>
|
|
@@ -135,7 +138,7 @@ export function ProductTable({ data }: { data: Product[] }) {
|
|
|
135
138
|
</tbody>
|
|
136
139
|
</table>
|
|
137
140
|
</div>
|
|
138
|
-
)
|
|
141
|
+
);
|
|
139
142
|
}
|
|
140
143
|
```
|
|
141
144
|
|
|
@@ -145,17 +148,17 @@ Wire `rowContextValue` into your own row/cell components for edit, selection, ex
|
|
|
145
148
|
|
|
146
149
|
Cell selection ships with clipboard shortcuts. The table parses TSV and emits structured payloads; **your app applies domain conversion and updates data**.
|
|
147
150
|
|
|
148
|
-
| Shortcut
|
|
149
|
-
|
|
|
150
|
-
| Ctrl/Cmd+C
|
|
151
|
-
| Ctrl/Cmd+Shift+C | Copy including collapsed tree descendants (`enableSubtreeCopy`)
|
|
152
|
-
| Ctrl/Cmd+V
|
|
151
|
+
| Shortcut | Behavior |
|
|
152
|
+
| ---------------- | -------------------------------------------------------------------------------------- |
|
|
153
|
+
| Ctrl/Cmd+C | Copy the active selection (visible rows) |
|
|
154
|
+
| Ctrl/Cmd+Shift+C | Copy including collapsed tree descendants (`enableSubtreeCopy`) |
|
|
155
|
+
| Ctrl/Cmd+V | Paste **overwrite** into the selection (`onRowsPaste`, `mode: "overwrite"`) |
|
|
153
156
|
| Ctrl/Cmd+Shift+V | Paste **insert** rows after the selection (`mode: "insert"`, when `enableInsertPaste`) |
|
|
154
157
|
|
|
155
158
|
Subtree copy encodes relative tree depth as leading tabs in the TSV so paste can rebuild parent/child nesting via `payload.depths`. Depth is only inferred when the clipboard looks like subtree indentation (first row unindented, at least one later row indented). Otherwise leading empty cells are kept as real values (e.g. Excel/Sheets blank first column) and `depths` stay `0`.
|
|
156
159
|
|
|
157
160
|
```tsx
|
|
158
|
-
import type { RowsPastePayload } from "react-glide-table/compound"
|
|
161
|
+
import type { RowsPastePayload } from "react-glide-table/compound";
|
|
159
162
|
|
|
160
163
|
<ProductTable
|
|
161
164
|
data={data}
|
|
@@ -164,27 +167,88 @@ import type { RowsPastePayload } from "react-glide-table/compound"
|
|
|
164
167
|
onRowsPaste={(payload: RowsPastePayload) => {
|
|
165
168
|
// overwrite: update cells from startRow using payload.values / columnIds
|
|
166
169
|
// insert: create rows after payload.endRow (or payload.anchorRowId for trees)
|
|
167
|
-
setData((prev) => applyMyPaste(prev, payload))
|
|
170
|
+
setData((prev) => applyMyPaste(prev, payload));
|
|
168
171
|
}}
|
|
169
172
|
>
|
|
170
173
|
{/* columns… */}
|
|
171
|
-
</ProductTable
|
|
174
|
+
</ProductTable>;
|
|
172
175
|
```
|
|
173
176
|
|
|
174
177
|
Related props: `onRowsPaste`, `enableInsertPaste`, `enableSubtreeCopy`, `onCopyActionsReady`.
|
|
175
178
|
Helpers (`/core`): `buildRowsPastePayload`, `parseClipboardTSV`, `parseClipboardTSVWithDepths`, `serializeSelectionToTSV`, …
|
|
176
179
|
|
|
180
|
+
## Column resize
|
|
181
|
+
|
|
182
|
+
Opt in with `enableColumnResize`. Drag the handle on the right edge of a header cell; double-click resets to the column’s default `width` / `size`.
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<ProductTable
|
|
186
|
+
data={data}
|
|
187
|
+
enableColumnResize
|
|
188
|
+
// optional controlled sizing
|
|
189
|
+
// columnSizing={sizing}
|
|
190
|
+
// onColumnSizingChange={setSizing}
|
|
191
|
+
>
|
|
192
|
+
<ProductTable.Header>
|
|
193
|
+
<ProductTable.Column field="name" width={200} minWidth={80} maxWidth={480}>
|
|
194
|
+
Name
|
|
195
|
+
</ProductTable.Column>
|
|
196
|
+
<ProductTable.Column field="sku" resizable={false}>
|
|
197
|
+
SKU
|
|
198
|
+
</ProductTable.Column>
|
|
199
|
+
</ProductTable.Header>
|
|
200
|
+
</ProductTable>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
| Prop | Role |
|
|
204
|
+
| ---------------------------------------- | -------------------------------------------- |
|
|
205
|
+
| `enableColumnResize` | Turn on header drag resize (default `false`) |
|
|
206
|
+
| `columnSizing` / `onColumnSizingChange` | Controlled width map `{ [columnId]: px }` |
|
|
207
|
+
| `columnResizeMode` | `"onChange"` (live) or `"onEnd"` |
|
|
208
|
+
| `Column.width` / `minWidth` / `maxWidth` | Default / clamp sizes |
|
|
209
|
+
| `Column.resizable={false}` | Disable resize for one column |
|
|
210
|
+
| `classNames.resizeHandle` | Style hook for the drag handle |
|
|
211
|
+
|
|
212
|
+
## Column freeze
|
|
213
|
+
|
|
214
|
+
Opt in with `enableColumnFreeze`. Mark columns with `frozen` — sticky insets are stacked so frozen cells never overlap, and **column order is unchanged** (middle columns may also freeze).
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<ProductTable data={data} enableColumnFreeze>
|
|
218
|
+
<ProductTable.Header>
|
|
219
|
+
<ProductTable.Column field="name" frozen width={200}>
|
|
220
|
+
Name
|
|
221
|
+
</ProductTable.Column>
|
|
222
|
+
<ProductTable.Column field="sku">SKU</ProductTable.Column>
|
|
223
|
+
<ProductTable.Column field="qty" frozen="left">
|
|
224
|
+
Qty
|
|
225
|
+
</ProductTable.Column>
|
|
226
|
+
<ProductTable.Column field="status" frozen="right">
|
|
227
|
+
Status
|
|
228
|
+
</ProductTable.Column>
|
|
229
|
+
</ProductTable.Header>
|
|
230
|
+
</ProductTable>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
| Prop | Role |
|
|
234
|
+
| ---------------------------------- | --------------------------------------- |
|
|
235
|
+
| `enableColumnFreeze` | Turn on sticky freeze (default `false`) |
|
|
236
|
+
| `Column.frozen` / `meta.frozen` | `true` / `"left"` or `"right"` |
|
|
237
|
+
| `data-frozen` / `data-freeze-edge` | State hooks for custom styling |
|
|
238
|
+
|
|
239
|
+
Helpers (`/core`): `buildColumnFreezeOffsets`, `getColumnFreezeStyle`, `resolveColumnFreezeSide`.
|
|
240
|
+
|
|
177
241
|
## Public API
|
|
178
242
|
|
|
179
|
-
| Export
|
|
180
|
-
|
|
|
181
|
-
| `createTable` / `Table`
|
|
182
|
-
| `DataTable`
|
|
183
|
-
| `useGlideTable`
|
|
184
|
-
| `useCellEdit` / `useCellSelection` / `useConvertTreeData`
|
|
185
|
-
| `applyCellEdit`, `applyFillData`, `buildRowsPastePayload`, `buildColumnRowSpanMap`, … | `/core`
|
|
186
|
-
| `DEFAULT_DATA_TABLE_LABELS` / `resolveDataTableLabels`
|
|
187
|
-
| Tree field defaults
|
|
243
|
+
| Export | Path | Role |
|
|
244
|
+
| ------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
|
|
245
|
+
| `createTable` / `Table` | `/compound` | Compound column DSL (`Header` / `Column` / `Body` / `Pagination`) |
|
|
246
|
+
| `DataTable` | `/compound` | Unstyled default renderer (semantic HTML + slots/props) |
|
|
247
|
+
| `useGlideTable` | `/core` | Headless engine escape hatch |
|
|
248
|
+
| `useCellEdit` / `useCellSelection` / `useConvertTreeData` | `/core` | Feature hooks |
|
|
249
|
+
| `applyCellEdit`, `applyFillData`, `buildRowsPastePayload`, `buildColumnRowSpanMap`, … | `/core` | Pure helpers |
|
|
250
|
+
| `DEFAULT_DATA_TABLE_LABELS` / `resolveDataTableLabels` | `/core` | Optional English UI copy helpers |
|
|
251
|
+
| Tree field defaults | `/core` | `id` / `parentId` / `children` / `qty` |
|
|
188
252
|
|
|
189
253
|
Root `react-glide-table` re-exports both surfaces. Related types: `TableProps`, `TableColumnProps`, `DataTableProps`, `DataTableSlots`, `TableCompoundComponent`, `ColumnDef`, `RowsPastePayload`, `PasteMode`, …
|
|
190
254
|
|
|
@@ -192,7 +256,7 @@ Root `react-glide-table` re-exports both surfaces. Related types: `TableProps`,
|
|
|
192
256
|
|
|
193
257
|
- **Row span + virtualization**: when `enableRowSpan` is on, virtualization is forced off (HTML `<table>` + `rowspan` cannot safely share a virtual window).
|
|
194
258
|
- **Paste is app-owned**: the library does not mutate `data` on paste — handle `onRowsPaste` (coerce types, ids, tree shape, row-span keys).
|
|
195
|
-
- **Flat tree parent order**: `useConvertTreeData` attaches each child to the nearest
|
|
259
|
+
- **Flat tree parent order**: `useConvertTreeData` attaches each child to the nearest _preceding_ row whose toggle key matches `parentId` (duplicate keys after paste resolve this way). Flat inputs must list parents before their children; a child whose parent appears later becomes a root. Nested `children` arrays are flattened parent-before-child automatically.
|
|
196
260
|
- **No shipped CSS**: the default renderer emits class hooks only. Bring your own styles (see playground for a CSS-skinned example).
|
|
197
261
|
|
|
198
262
|
## Local playground
|
package/dist/compound.cjs
CHANGED
|
@@ -45,6 +45,9 @@ var ROW_HOVERED_BG_CLASS = "row-hovered";
|
|
|
45
45
|
var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
|
|
46
46
|
var DATA_TABLE_ROW_HEIGHT = 44;
|
|
47
47
|
var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
|
|
48
|
+
var DATA_TABLE_COLUMN_SIZE = 150;
|
|
49
|
+
var DATA_TABLE_COLUMN_MIN_SIZE = 40;
|
|
50
|
+
var DATA_TABLE_COLUMN_MAX_SIZE = 800;
|
|
48
51
|
|
|
49
52
|
// src/components/ui/table/DataTableContext.tsx
|
|
50
53
|
var import_react = require("react");
|
|
@@ -385,6 +388,81 @@ function hasCellSelectionEdges(style) {
|
|
|
385
388
|
);
|
|
386
389
|
}
|
|
387
390
|
|
|
391
|
+
// src/components/ui/table/features/column-freeze/columnFreeze.ts
|
|
392
|
+
var HEADER_Z_BASE = 30;
|
|
393
|
+
var BODY_Z_BASE = 5;
|
|
394
|
+
function resolveColumnFreezeSide(frozen) {
|
|
395
|
+
if (frozen === true || frozen === "left") return "left";
|
|
396
|
+
if (frozen === "right") return "right";
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
function buildColumnFreezeOffsets(columns) {
|
|
400
|
+
const result = /* @__PURE__ */ new Map();
|
|
401
|
+
let leftOffset = 0;
|
|
402
|
+
const leftIds = [];
|
|
403
|
+
for (const column of columns) {
|
|
404
|
+
if (column.side !== "left") continue;
|
|
405
|
+
leftIds.push(column.id);
|
|
406
|
+
result.set(column.id, {
|
|
407
|
+
side: "left",
|
|
408
|
+
offset: leftOffset,
|
|
409
|
+
isEdge: false,
|
|
410
|
+
stack: 0
|
|
411
|
+
});
|
|
412
|
+
leftOffset += column.size;
|
|
413
|
+
}
|
|
414
|
+
leftIds.forEach((id, index) => {
|
|
415
|
+
const entry = result.get(id);
|
|
416
|
+
if (!entry) return;
|
|
417
|
+
entry.isEdge = index === leftIds.length - 1;
|
|
418
|
+
entry.stack = leftIds.length - index;
|
|
419
|
+
});
|
|
420
|
+
let rightOffset = 0;
|
|
421
|
+
const rightIds = [];
|
|
422
|
+
for (let index = columns.length - 1; index >= 0; index -= 1) {
|
|
423
|
+
const column = columns[index];
|
|
424
|
+
if (!column || column.side !== "right") continue;
|
|
425
|
+
rightIds.push(column.id);
|
|
426
|
+
result.set(column.id, {
|
|
427
|
+
side: "right",
|
|
428
|
+
offset: rightOffset,
|
|
429
|
+
isEdge: false,
|
|
430
|
+
stack: 0
|
|
431
|
+
});
|
|
432
|
+
rightOffset += column.size;
|
|
433
|
+
}
|
|
434
|
+
rightIds.forEach((id, index) => {
|
|
435
|
+
const entry = result.get(id);
|
|
436
|
+
if (!entry) return;
|
|
437
|
+
entry.isEdge = index === rightIds.length - 1;
|
|
438
|
+
entry.stack = rightIds.length - index;
|
|
439
|
+
});
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
function getColumnFreezeStyle(offset, options) {
|
|
443
|
+
if (!offset) return void 0;
|
|
444
|
+
const zBase = options?.isHeader ? HEADER_Z_BASE : BODY_Z_BASE;
|
|
445
|
+
return {
|
|
446
|
+
position: "sticky",
|
|
447
|
+
...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
|
|
448
|
+
zIndex: zBase + offset.stack,
|
|
449
|
+
...options?.isHeader ? { top: 0 } : {}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/components/ui/table/features/column-resize/columnResize.ts
|
|
454
|
+
function getColumnSizeStyle(size, options) {
|
|
455
|
+
const { force = false, lockMax = false } = options ?? {};
|
|
456
|
+
if (!force && size === DATA_TABLE_COLUMN_SIZE) {
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
width: size,
|
|
461
|
+
minWidth: size,
|
|
462
|
+
...lockMax ? { maxWidth: size } : {}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
388
466
|
// src/components/ui/table/features/row-expand/row-expand.ts
|
|
389
467
|
var import_react2 = require("react");
|
|
390
468
|
|
|
@@ -822,7 +900,18 @@ function DataTableRow({
|
|
|
822
900
|
virtualIndex,
|
|
823
901
|
measureElement
|
|
824
902
|
}) {
|
|
825
|
-
const {
|
|
903
|
+
const {
|
|
904
|
+
classNames,
|
|
905
|
+
rowSpan,
|
|
906
|
+
selection,
|
|
907
|
+
cellSelection,
|
|
908
|
+
cellEdit,
|
|
909
|
+
expand,
|
|
910
|
+
columnResize,
|
|
911
|
+
columnFreeze
|
|
912
|
+
} = useDataTableRowContext();
|
|
913
|
+
const { enableColumnResize } = columnResize;
|
|
914
|
+
const { enableColumnFreeze, offsets: freezeOffsets } = columnFreeze;
|
|
826
915
|
const {
|
|
827
916
|
enableRowSpan,
|
|
828
917
|
primaryRowSpanColumnId,
|
|
@@ -1003,6 +1092,17 @@ function DataTableRow({
|
|
|
1003
1092
|
);
|
|
1004
1093
|
const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
|
|
1005
1094
|
const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
|
|
1095
|
+
const sizeStyle = getColumnSizeStyle(cell.column.getSize(), {
|
|
1096
|
+
force: enableColumnResize,
|
|
1097
|
+
lockMax: enableColumnResize
|
|
1098
|
+
});
|
|
1099
|
+
const freezeOffset = enableColumnFreeze ? freezeOffsets.get(columnId) : void 0;
|
|
1100
|
+
const freezeStyle = getColumnFreezeStyle(freezeOffset);
|
|
1101
|
+
const cellStyle = {
|
|
1102
|
+
...sizeStyle,
|
|
1103
|
+
...freezeStyle,
|
|
1104
|
+
...selectionEdgeStyle
|
|
1105
|
+
};
|
|
1006
1106
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1007
1107
|
"td",
|
|
1008
1108
|
{
|
|
@@ -1016,6 +1116,8 @@ function DataTableRow({
|
|
|
1016
1116
|
"data-selection-edges": hasSelectionEdges ? "" : void 0,
|
|
1017
1117
|
"data-editable": editable ? "" : void 0,
|
|
1018
1118
|
"data-editing": isEditing ? "" : void 0,
|
|
1119
|
+
"data-frozen": freezeOffset?.side,
|
|
1120
|
+
"data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
|
|
1019
1121
|
onMouseDown: (event) => {
|
|
1020
1122
|
if (isEditing) {
|
|
1021
1123
|
event.stopPropagation();
|
|
@@ -1050,9 +1152,10 @@ function DataTableRow({
|
|
|
1050
1152
|
event.stopPropagation();
|
|
1051
1153
|
onStartEdit(rowIndex, cellIndex);
|
|
1052
1154
|
},
|
|
1053
|
-
style:
|
|
1155
|
+
style: Object.keys(cellStyle).length > 0 ? cellStyle : void 0,
|
|
1054
1156
|
className: cn(
|
|
1055
1157
|
"data-table-cell",
|
|
1158
|
+
freezeOffset && `data-table-cell--frozen-${freezeOffset.side}`,
|
|
1056
1159
|
CELL_ALIGN_CLASS[align],
|
|
1057
1160
|
cellClassName,
|
|
1058
1161
|
isMerged && "is-merged",
|
|
@@ -1803,7 +1906,8 @@ var DEFAULT_DATA_TABLE_LABELS = {
|
|
|
1803
1906
|
loading: "Loading...",
|
|
1804
1907
|
selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
|
|
1805
1908
|
expandRow: "Expand row",
|
|
1806
|
-
collapseRow: "Collapse row"
|
|
1909
|
+
collapseRow: "Collapse row",
|
|
1910
|
+
resizeColumn: "Resize column"
|
|
1807
1911
|
};
|
|
1808
1912
|
function resolveDataTableLabels(partial) {
|
|
1809
1913
|
return {
|
|
@@ -1813,6 +1917,7 @@ function resolveDataTableLabels(partial) {
|
|
|
1813
1917
|
}
|
|
1814
1918
|
|
|
1815
1919
|
// src/core/useGlideTable.ts
|
|
1920
|
+
var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
|
|
1816
1921
|
function useGlideTable(options) {
|
|
1817
1922
|
const {
|
|
1818
1923
|
data,
|
|
@@ -1848,7 +1953,12 @@ function useGlideTable(options) {
|
|
|
1848
1953
|
enableInsertPaste,
|
|
1849
1954
|
enableVirtualization = true,
|
|
1850
1955
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
1851
|
-
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
1956
|
+
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN,
|
|
1957
|
+
enableColumnResize = false,
|
|
1958
|
+
columnSizing: controlledColumnSizing,
|
|
1959
|
+
onColumnSizingChange,
|
|
1960
|
+
columnResizeMode = "onChange",
|
|
1961
|
+
enableColumnFreeze = false
|
|
1852
1962
|
} = options;
|
|
1853
1963
|
const labels = (0, import_react6.useMemo)(() => {
|
|
1854
1964
|
const resolved = resolveDataTableLabels(labelsProp);
|
|
@@ -1862,6 +1972,7 @@ function useGlideTable(options) {
|
|
|
1862
1972
|
const enableExpand = Boolean(toggleField);
|
|
1863
1973
|
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
1864
1974
|
const [internalRowSelection, setInternalRowSelection] = (0, import_react6.useState)({});
|
|
1975
|
+
const [internalColumnSizing, setInternalColumnSizing] = (0, import_react6.useState)({});
|
|
1865
1976
|
const [internalExpandedRows, setInternalExpandedRows] = (0, import_react6.useState)(
|
|
1866
1977
|
() => /* @__PURE__ */ new Set()
|
|
1867
1978
|
);
|
|
@@ -1880,6 +1991,7 @@ function useGlideTable(options) {
|
|
|
1880
1991
|
controlledRowSelection,
|
|
1881
1992
|
internalRowSelection
|
|
1882
1993
|
);
|
|
1994
|
+
const columnSizing = controlledColumnSizing ?? internalColumnSizing;
|
|
1883
1995
|
const expandedRows = controlledExpandedRows ?? internalExpandedRows;
|
|
1884
1996
|
const handleExpandedRowsChange = (0, import_react6.useCallback)(
|
|
1885
1997
|
(next) => {
|
|
@@ -1905,9 +2017,27 @@ function useGlideTable(options) {
|
|
|
1905
2017
|
const table = (0, import_react_table2.useReactTable)({
|
|
1906
2018
|
data: tableData,
|
|
1907
2019
|
columns,
|
|
2020
|
+
...enableColumnResize ? {
|
|
2021
|
+
defaultColumn: {
|
|
2022
|
+
minSize: DATA_TABLE_COLUMN_MIN_SIZE,
|
|
2023
|
+
maxSize: DATA_TABLE_COLUMN_MAX_SIZE
|
|
2024
|
+
}
|
|
2025
|
+
} : {},
|
|
2026
|
+
enableColumnResizing: enableColumnResize,
|
|
2027
|
+
columnResizeMode,
|
|
1908
2028
|
state: {
|
|
1909
|
-
rowSelection: rowSelectionMode === "none" ? {} : rowSelection
|
|
2029
|
+
rowSelection: rowSelectionMode === "none" ? {} : rowSelection,
|
|
2030
|
+
...enableColumnResize ? { columnSizing } : {}
|
|
1910
2031
|
},
|
|
2032
|
+
onColumnSizingChange: enableColumnResize ? (updater) => {
|
|
2033
|
+
if (onColumnSizingChange) {
|
|
2034
|
+
onColumnSizingChange(updater);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
setInternalColumnSizing(
|
|
2038
|
+
(previous) => typeof updater === "function" ? updater(previous) : updater
|
|
2039
|
+
);
|
|
2040
|
+
} : void 0,
|
|
1911
2041
|
enableRowSelection: rowSelectionMode === "none" ? false : getRowCanSelect ? (row) => getRowCanSelect(row.original, row.index) : true,
|
|
1912
2042
|
enableMultiRowSelection: rowSelectionMode === "multi",
|
|
1913
2043
|
onRowSelectionChange: (updater) => {
|
|
@@ -1938,6 +2068,17 @@ function useGlideTable(options) {
|
|
|
1938
2068
|
const selectedCount = selectedRows.length;
|
|
1939
2069
|
const rows = table.getRowModel().rows;
|
|
1940
2070
|
const columnCount = table.getAllLeafColumns().length || 1;
|
|
2071
|
+
const visibleLeafColumns = table.getVisibleLeafColumns();
|
|
2072
|
+
const columnFreezeOffsets = (0, import_react6.useMemo)(() => {
|
|
2073
|
+
if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
|
|
2074
|
+
return buildColumnFreezeOffsets(
|
|
2075
|
+
visibleLeafColumns.map((column) => ({
|
|
2076
|
+
id: column.id,
|
|
2077
|
+
size: column.getSize(),
|
|
2078
|
+
side: resolveColumnFreezeSide(column.columnDef.meta?.frozen)
|
|
2079
|
+
}))
|
|
2080
|
+
);
|
|
2081
|
+
}, [enableColumnFreeze, visibleLeafColumns, columnSizing]);
|
|
1941
2082
|
const rowVirtualizer = (0, import_react_virtual.useVirtualizer)({
|
|
1942
2083
|
count: shouldVirtualize ? rows.length : 0,
|
|
1943
2084
|
getScrollElement: () => scrollRef.current,
|
|
@@ -2054,6 +2195,13 @@ function useGlideTable(options) {
|
|
|
2054
2195
|
onToggleExpand: handleToggleExpand,
|
|
2055
2196
|
expandRowLabel: labels.expandRow,
|
|
2056
2197
|
collapseRowLabel: labels.collapseRow
|
|
2198
|
+
},
|
|
2199
|
+
columnResize: {
|
|
2200
|
+
enableColumnResize
|
|
2201
|
+
},
|
|
2202
|
+
columnFreeze: {
|
|
2203
|
+
enableColumnFreeze,
|
|
2204
|
+
offsets: columnFreezeOffsets
|
|
2057
2205
|
}
|
|
2058
2206
|
};
|
|
2059
2207
|
}, [
|
|
@@ -2086,7 +2234,10 @@ function useGlideTable(options) {
|
|
|
2086
2234
|
preventExpand,
|
|
2087
2235
|
handleToggleExpand,
|
|
2088
2236
|
labels.expandRow,
|
|
2089
|
-
labels.collapseRow
|
|
2237
|
+
labels.collapseRow,
|
|
2238
|
+
enableColumnResize,
|
|
2239
|
+
enableColumnFreeze,
|
|
2240
|
+
columnFreezeOffsets
|
|
2090
2241
|
]);
|
|
2091
2242
|
const copySelectionRef = (0, import_react6.useRef)(copySelection);
|
|
2092
2243
|
(0, import_react6.useEffect)(() => {
|
|
@@ -2107,6 +2258,8 @@ function useGlideTable(options) {
|
|
|
2107
2258
|
loadingText: labels.loading,
|
|
2108
2259
|
selectionLabel: labels.selection,
|
|
2109
2260
|
enableCellSelection,
|
|
2261
|
+
enableColumnResize,
|
|
2262
|
+
enableColumnFreeze,
|
|
2110
2263
|
shouldVirtualize,
|
|
2111
2264
|
scrollRef,
|
|
2112
2265
|
rowVirtualizer,
|
|
@@ -2172,10 +2325,13 @@ function DataTable({
|
|
|
2172
2325
|
rows,
|
|
2173
2326
|
columnCount,
|
|
2174
2327
|
selectedCount,
|
|
2328
|
+
labels,
|
|
2175
2329
|
emptyText,
|
|
2176
2330
|
loadingText,
|
|
2177
2331
|
selectionLabel,
|
|
2178
2332
|
enableCellSelection,
|
|
2333
|
+
enableColumnResize,
|
|
2334
|
+
enableColumnFreeze,
|
|
2179
2335
|
shouldVirtualize,
|
|
2180
2336
|
scrollRef,
|
|
2181
2337
|
rowVirtualizer,
|
|
@@ -2190,6 +2346,7 @@ function DataTable({
|
|
|
2190
2346
|
const RowSlot = slots?.Row ?? DataTableRow;
|
|
2191
2347
|
const PendingSlot = slots?.Pending ?? DefaultPending;
|
|
2192
2348
|
const EmptySlot = slots?.Empty ?? DefaultEmpty;
|
|
2349
|
+
const freezeOffsets = rowContextValue.columnFreeze.offsets;
|
|
2193
2350
|
const contextValue = (0, import_react7.useMemo)(
|
|
2194
2351
|
() => ({ ...rowContextValue, classNames }),
|
|
2195
2352
|
[rowContextValue, classNames]
|
|
@@ -2210,6 +2367,8 @@ function DataTable({
|
|
|
2210
2367
|
className: cn(
|
|
2211
2368
|
"DataTableJSX",
|
|
2212
2369
|
!enableCellSelection && "DataTableJSX--no-cell-selection",
|
|
2370
|
+
enableColumnResize && "DataTableJSX--column-resize",
|
|
2371
|
+
enableColumnFreeze && "DataTableJSX--column-freeze",
|
|
2213
2372
|
classNames?.root,
|
|
2214
2373
|
className
|
|
2215
2374
|
),
|
|
@@ -2230,6 +2389,7 @@ function DataTable({
|
|
|
2230
2389
|
"table",
|
|
2231
2390
|
{
|
|
2232
2391
|
className: cn("data-table", classNames?.table),
|
|
2392
|
+
style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
|
|
2233
2393
|
onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
|
|
2234
2394
|
children: [
|
|
2235
2395
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
@@ -2239,22 +2399,54 @@ function DataTable({
|
|
|
2239
2399
|
children: headerGroup.headers.map((header) => {
|
|
2240
2400
|
const align = header.column.columnDef.meta?.align ?? "center";
|
|
2241
2401
|
const headerClassName = header.column.columnDef.meta?.headerClassName;
|
|
2242
|
-
|
|
2402
|
+
const canResize = enableColumnResize && header.column.getCanResize();
|
|
2403
|
+
const sizeStyle = getColumnSizeStyle(header.getSize(), {
|
|
2404
|
+
force: enableColumnResize,
|
|
2405
|
+
lockMax: enableColumnResize
|
|
2406
|
+
});
|
|
2407
|
+
const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
|
|
2408
|
+
const freezeStyle = getColumnFreezeStyle(freezeOffset, {
|
|
2409
|
+
isHeader: true
|
|
2410
|
+
});
|
|
2411
|
+
const headerStyle = {
|
|
2412
|
+
...sizeStyle,
|
|
2413
|
+
...freezeStyle
|
|
2414
|
+
};
|
|
2415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2243
2416
|
"th",
|
|
2244
2417
|
{
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
minWidth: header.getSize() !== 150 ? header.getSize() : void 0
|
|
2250
|
-
},
|
|
2418
|
+
"data-resizing": header.column.getIsResizing() ? "" : void 0,
|
|
2419
|
+
"data-frozen": freezeOffset?.side,
|
|
2420
|
+
"data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
|
|
2421
|
+
style: Object.keys(headerStyle).length > 0 ? headerStyle : void 0,
|
|
2251
2422
|
className: cn(
|
|
2252
2423
|
"data-table-head-cell",
|
|
2424
|
+
freezeOffset && `data-table-head-cell--frozen-${freezeOffset.side}`,
|
|
2253
2425
|
CELL_ALIGN_CLASS[align],
|
|
2254
2426
|
classNames?.headCell,
|
|
2255
2427
|
headerClassName
|
|
2256
2428
|
),
|
|
2257
|
-
children:
|
|
2429
|
+
children: [
|
|
2430
|
+
header.isPlaceholder ? null : (0, import_react_table3.flexRender)(header.column.columnDef.header, header.getContext()),
|
|
2431
|
+
canResize ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2432
|
+
"div",
|
|
2433
|
+
{
|
|
2434
|
+
role: "separator",
|
|
2435
|
+
"aria-orientation": "vertical",
|
|
2436
|
+
"aria-label": labels.resizeColumn,
|
|
2437
|
+
"data-table-disable-cell-selection": "",
|
|
2438
|
+
className: cn(
|
|
2439
|
+
"data-table-resize-handle",
|
|
2440
|
+
classNames?.resizeHandle
|
|
2441
|
+
),
|
|
2442
|
+
onMouseDown: header.getResizeHandler(),
|
|
2443
|
+
onTouchStart: header.getResizeHandler(),
|
|
2444
|
+
onDoubleClick: () => {
|
|
2445
|
+
header.column.resetSize();
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
) : null
|
|
2449
|
+
]
|
|
2258
2450
|
},
|
|
2259
2451
|
header.id
|
|
2260
2452
|
);
|
|
@@ -2382,6 +2574,10 @@ function buildColumnDef(props, sort, onSort) {
|
|
|
2382
2574
|
children,
|
|
2383
2575
|
sortable = false,
|
|
2384
2576
|
width,
|
|
2577
|
+
minWidth,
|
|
2578
|
+
maxWidth,
|
|
2579
|
+
resizable,
|
|
2580
|
+
frozen,
|
|
2385
2581
|
align,
|
|
2386
2582
|
rowSpan,
|
|
2387
2583
|
rowSpanKey,
|
|
@@ -2395,7 +2591,10 @@ function buildColumnDef(props, sort, onSort) {
|
|
|
2395
2591
|
return {
|
|
2396
2592
|
id: field,
|
|
2397
2593
|
...!virtual ? { accessorKey: field } : {},
|
|
2398
|
-
size: width ??
|
|
2594
|
+
size: width ?? DATA_TABLE_COLUMN_SIZE,
|
|
2595
|
+
...minWidth != null ? { minSize: minWidth } : {},
|
|
2596
|
+
...maxWidth != null ? { maxSize: maxWidth } : {},
|
|
2597
|
+
...resizable === false ? { enableResizing: false } : {},
|
|
2399
2598
|
header: sortable ? () => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SortableHeader, { label: children, field, sort, onSort }) : (
|
|
2400
2599
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
2401
2600
|
() => children
|
|
@@ -2415,6 +2614,7 @@ function buildColumnDef(props, sort, onSort) {
|
|
|
2415
2614
|
editable,
|
|
2416
2615
|
editType,
|
|
2417
2616
|
editInputProps,
|
|
2617
|
+
frozen,
|
|
2418
2618
|
className,
|
|
2419
2619
|
headerClassName
|
|
2420
2620
|
}
|
package/dist/compound.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, ReactElement } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { a as
|
|
5
|
-
export { ColumnDef, RowSelectionState } from '@tanstack/react-table';
|
|
3
|
+
import { g as DataTableProps, T as TableColumnProps, j as TableProps } from './types-D031_07N.cjs';
|
|
4
|
+
export { a as ColumnFreezeMeta, c as ColumnFreezeSide, d as DataTableClassNames, f as DataTableLabels, h as DataTableSlots, P as PasteMode, R as RowSelectionMode, i as RowsPastePayload } from './types-D031_07N.cjs';
|
|
5
|
+
export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Unstyled DataTable shell: semantic HTML + interaction behavior.
|