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 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 | 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) |
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 | 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 |
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(header.column.columnDef.header, header.getContext())}
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 | Behavior |
149
- | --- | --- |
150
- | Ctrl/Cmd+C | Copy the active selection (visible rows) |
151
- | Ctrl/Cmd+Shift+C | Copy including collapsed tree descendants (`enableSubtreeCopy`) |
152
- | Ctrl/Cmd+V | Paste **overwrite** into the selection (`onRowsPaste`, `mode: "overwrite"`) |
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 | Path | Role |
180
- | --- | --- | --- |
181
- | `createTable` / `Table` | `/compound` | Compound column DSL (`Header` / `Column` / `Body` / `Pagination`) |
182
- | `DataTable` | `/compound` | Unstyled default renderer (semantic HTML + slots/props) |
183
- | `useGlideTable` | `/core` | Headless engine escape hatch |
184
- | `useCellEdit` / `useCellSelection` / `useConvertTreeData` | `/core` | Feature hooks |
185
- | `applyCellEdit`, `applyFillData`, `buildRowsPastePayload`, `buildColumnRowSpanMap`, … | `/core` | Pure helpers |
186
- | `DEFAULT_DATA_TABLE_LABELS` / `resolveDataTableLabels` | `/core` | Optional English UI copy helpers |
187
- | Tree field defaults | `/core` | `id` / `parentId` / `children` / `qty` |
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 *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.
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 { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
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: selectionEdgeStyle,
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
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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
- style: {
2246
- width: header.getSize() !== 150 ? header.getSize() : void 0,
2247
- // Column sizing follows TanStack's `size`, but
2248
- // ensure the column keeps its min width when the container shrinks.
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: header.isPlaceholder ? null : (0, import_react_table3.flexRender)(header.column.columnDef.header, header.getContext())
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 ?? 150,
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
  }
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { d as DataTableProps, T as TableColumnProps, g as TableProps } from './types-xxzMLUwM.cjs';
4
- export { a as DataTableClassNames, c as DataTableLabels, e as DataTableSlots, P as PasteMode, R as RowSelectionMode, f as RowsPastePayload } from './types-xxzMLUwM.cjs';
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.