react-glide-table 1.1.8 → 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/dist/index.js CHANGED
@@ -4,7 +4,8 @@ var DEFAULT_DATA_TABLE_LABELS = {
4
4
  loading: "Loading...",
5
5
  selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
6
6
  expandRow: "Expand row",
7
- collapseRow: "Collapse row"
7
+ collapseRow: "Collapse row",
8
+ resizeColumn: "Resize column"
8
9
  };
9
10
  function resolveDataTableLabels(partial) {
10
11
  return {
@@ -46,6 +47,9 @@ var ROW_HOVERED_BG_CLASS = "row-hovered";
46
47
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
47
48
  var DATA_TABLE_ROW_HEIGHT = 44;
48
49
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
50
+ var DATA_TABLE_COLUMN_SIZE = 150;
51
+ var DATA_TABLE_COLUMN_MIN_SIZE = 40;
52
+ var DATA_TABLE_COLUMN_MAX_SIZE = 800;
49
53
 
50
54
  // src/components/ui/table/features/cell-edit/useCellEdit.ts
51
55
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -936,6 +940,68 @@ function useCellSelection({
936
940
  };
937
941
  }
938
942
 
943
+ // src/components/ui/table/features/column-freeze/columnFreeze.ts
944
+ var HEADER_Z_BASE = 30;
945
+ var BODY_Z_BASE = 5;
946
+ function resolveColumnFreezeSide(frozen) {
947
+ if (frozen === true || frozen === "left") return "left";
948
+ if (frozen === "right") return "right";
949
+ return void 0;
950
+ }
951
+ function buildColumnFreezeOffsets(columns) {
952
+ const result = /* @__PURE__ */ new Map();
953
+ let leftOffset = 0;
954
+ const leftIds = [];
955
+ for (const column of columns) {
956
+ if (column.side !== "left") continue;
957
+ leftIds.push(column.id);
958
+ result.set(column.id, {
959
+ side: "left",
960
+ offset: leftOffset,
961
+ isEdge: false,
962
+ stack: 0
963
+ });
964
+ leftOffset += column.size;
965
+ }
966
+ leftIds.forEach((id, index) => {
967
+ const entry = result.get(id);
968
+ if (!entry) return;
969
+ entry.isEdge = index === leftIds.length - 1;
970
+ entry.stack = leftIds.length - index;
971
+ });
972
+ let rightOffset = 0;
973
+ const rightIds = [];
974
+ for (let index = columns.length - 1; index >= 0; index -= 1) {
975
+ const column = columns[index];
976
+ if (!column || column.side !== "right") continue;
977
+ rightIds.push(column.id);
978
+ result.set(column.id, {
979
+ side: "right",
980
+ offset: rightOffset,
981
+ isEdge: false,
982
+ stack: 0
983
+ });
984
+ rightOffset += column.size;
985
+ }
986
+ rightIds.forEach((id, index) => {
987
+ const entry = result.get(id);
988
+ if (!entry) return;
989
+ entry.isEdge = index === rightIds.length - 1;
990
+ entry.stack = rightIds.length - index;
991
+ });
992
+ return result;
993
+ }
994
+ function getColumnFreezeStyle(offset, options) {
995
+ if (!offset) return void 0;
996
+ const zBase = options?.isHeader ? HEADER_Z_BASE : BODY_Z_BASE;
997
+ return {
998
+ position: "sticky",
999
+ ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1000
+ zIndex: zBase + offset.stack,
1001
+ ...options?.isHeader ? { top: 0 } : {}
1002
+ };
1003
+ }
1004
+
939
1005
  // src/components/ui/table/features/row-expand/row-expand.ts
940
1006
  import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
941
1007
  function getFieldValue(row, key) {
@@ -1201,6 +1267,7 @@ function collectRowSpanColumns(columns) {
1201
1267
  }
1202
1268
 
1203
1269
  // src/core/useGlideTable.ts
1270
+ var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
1204
1271
  function useGlideTable(options) {
1205
1272
  const {
1206
1273
  data,
@@ -1236,7 +1303,12 @@ function useGlideTable(options) {
1236
1303
  enableInsertPaste,
1237
1304
  enableVirtualization = true,
1238
1305
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
1239
- virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
1306
+ virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN,
1307
+ enableColumnResize = false,
1308
+ columnSizing: controlledColumnSizing,
1309
+ onColumnSizingChange,
1310
+ columnResizeMode = "onChange",
1311
+ enableColumnFreeze = false
1240
1312
  } = options;
1241
1313
  const labels = useMemo2(() => {
1242
1314
  const resolved = resolveDataTableLabels(labelsProp);
@@ -1250,6 +1322,7 @@ function useGlideTable(options) {
1250
1322
  const enableExpand = Boolean(toggleField);
1251
1323
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1252
1324
  const [internalRowSelection, setInternalRowSelection] = useState3({});
1325
+ const [internalColumnSizing, setInternalColumnSizing] = useState3({});
1253
1326
  const [internalExpandedRows, setInternalExpandedRows] = useState3(
1254
1327
  () => /* @__PURE__ */ new Set()
1255
1328
  );
@@ -1268,6 +1341,7 @@ function useGlideTable(options) {
1268
1341
  controlledRowSelection,
1269
1342
  internalRowSelection
1270
1343
  );
1344
+ const columnSizing = controlledColumnSizing ?? internalColumnSizing;
1271
1345
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
1272
1346
  const handleExpandedRowsChange = useCallback3(
1273
1347
  (next) => {
@@ -1293,9 +1367,27 @@ function useGlideTable(options) {
1293
1367
  const table = useReactTable({
1294
1368
  data: tableData,
1295
1369
  columns,
1370
+ ...enableColumnResize ? {
1371
+ defaultColumn: {
1372
+ minSize: DATA_TABLE_COLUMN_MIN_SIZE,
1373
+ maxSize: DATA_TABLE_COLUMN_MAX_SIZE
1374
+ }
1375
+ } : {},
1376
+ enableColumnResizing: enableColumnResize,
1377
+ columnResizeMode,
1296
1378
  state: {
1297
- rowSelection: rowSelectionMode === "none" ? {} : rowSelection
1379
+ rowSelection: rowSelectionMode === "none" ? {} : rowSelection,
1380
+ ...enableColumnResize ? { columnSizing } : {}
1298
1381
  },
1382
+ onColumnSizingChange: enableColumnResize ? (updater) => {
1383
+ if (onColumnSizingChange) {
1384
+ onColumnSizingChange(updater);
1385
+ return;
1386
+ }
1387
+ setInternalColumnSizing(
1388
+ (previous) => typeof updater === "function" ? updater(previous) : updater
1389
+ );
1390
+ } : void 0,
1299
1391
  enableRowSelection: rowSelectionMode === "none" ? false : getRowCanSelect ? (row) => getRowCanSelect(row.original, row.index) : true,
1300
1392
  enableMultiRowSelection: rowSelectionMode === "multi",
1301
1393
  onRowSelectionChange: (updater) => {
@@ -1326,6 +1418,17 @@ function useGlideTable(options) {
1326
1418
  const selectedCount = selectedRows.length;
1327
1419
  const rows = table.getRowModel().rows;
1328
1420
  const columnCount = table.getAllLeafColumns().length || 1;
1421
+ const visibleLeafColumns = table.getVisibleLeafColumns();
1422
+ const columnFreezeOffsets = useMemo2(() => {
1423
+ if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
1424
+ return buildColumnFreezeOffsets(
1425
+ visibleLeafColumns.map((column) => ({
1426
+ id: column.id,
1427
+ size: column.getSize(),
1428
+ side: resolveColumnFreezeSide(column.columnDef.meta?.frozen)
1429
+ }))
1430
+ );
1431
+ }, [enableColumnFreeze, visibleLeafColumns, columnSizing]);
1329
1432
  const rowVirtualizer = useVirtualizer({
1330
1433
  count: shouldVirtualize ? rows.length : 0,
1331
1434
  getScrollElement: () => scrollRef.current,
@@ -1442,6 +1545,13 @@ function useGlideTable(options) {
1442
1545
  onToggleExpand: handleToggleExpand,
1443
1546
  expandRowLabel: labels.expandRow,
1444
1547
  collapseRowLabel: labels.collapseRow
1548
+ },
1549
+ columnResize: {
1550
+ enableColumnResize
1551
+ },
1552
+ columnFreeze: {
1553
+ enableColumnFreeze,
1554
+ offsets: columnFreezeOffsets
1445
1555
  }
1446
1556
  };
1447
1557
  }, [
@@ -1474,7 +1584,10 @@ function useGlideTable(options) {
1474
1584
  preventExpand,
1475
1585
  handleToggleExpand,
1476
1586
  labels.expandRow,
1477
- labels.collapseRow
1587
+ labels.collapseRow,
1588
+ enableColumnResize,
1589
+ enableColumnFreeze,
1590
+ columnFreezeOffsets
1478
1591
  ]);
1479
1592
  const copySelectionRef = useRef4(copySelection);
1480
1593
  useEffect4(() => {
@@ -1495,6 +1608,8 @@ function useGlideTable(options) {
1495
1608
  loadingText: labels.loading,
1496
1609
  selectionLabel: labels.selection,
1497
1610
  enableCellSelection,
1611
+ enableColumnResize,
1612
+ enableColumnFreeze,
1498
1613
  shouldVirtualize,
1499
1614
  scrollRef,
1500
1615
  rowVirtualizer,
@@ -1508,6 +1623,19 @@ function useGlideTable(options) {
1508
1623
  };
1509
1624
  }
1510
1625
 
1626
+ // src/components/ui/table/features/column-resize/columnResize.ts
1627
+ function getColumnSizeStyle(size, options) {
1628
+ const { force = false, lockMax = false } = options ?? {};
1629
+ if (!force && size === DATA_TABLE_COLUMN_SIZE) {
1630
+ return void 0;
1631
+ }
1632
+ return {
1633
+ width: size,
1634
+ minWidth: size,
1635
+ ...lockMax ? { maxWidth: size } : {}
1636
+ };
1637
+ }
1638
+
1511
1639
  // src/components/ui/table/components/DataTable/DataTable.tsx
1512
1640
  import { flexRender as flexRender2 } from "@tanstack/react-table";
1513
1641
  import { useMemo as useMemo3 } from "react";
@@ -1681,6 +1809,19 @@ function cn(...inputs) {
1681
1809
 
1682
1810
  // src/components/ui/table/components/DataTable/DataTableRow.tsx
1683
1811
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1812
+ function isInteractiveMouseTarget(target) {
1813
+ if (!(target instanceof Element)) return false;
1814
+ const interactiveSelector = [
1815
+ "input",
1816
+ "textarea",
1817
+ "select",
1818
+ "button",
1819
+ "a[href]",
1820
+ "[contenteditable]:not([contenteditable='false'])",
1821
+ "[data-table-disable-cell-selection]"
1822
+ ].join(",");
1823
+ return target.closest(interactiveSelector) !== null;
1824
+ }
1684
1825
  function resolveExpandCellIndex(cells, toggleField) {
1685
1826
  if (!toggleField) return 0;
1686
1827
  const matchedIndex = cells.findIndex(
@@ -1701,7 +1842,18 @@ function DataTableRow({
1701
1842
  virtualIndex,
1702
1843
  measureElement
1703
1844
  }) {
1704
- const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
1845
+ const {
1846
+ classNames,
1847
+ rowSpan,
1848
+ selection,
1849
+ cellSelection,
1850
+ cellEdit,
1851
+ expand,
1852
+ columnResize,
1853
+ columnFreeze
1854
+ } = useDataTableRowContext();
1855
+ const { enableColumnResize } = columnResize;
1856
+ const { enableColumnFreeze, offsets: freezeOffsets } = columnFreeze;
1705
1857
  const {
1706
1858
  enableRowSpan,
1707
1859
  primaryRowSpanColumnId,
@@ -1747,6 +1899,33 @@ function DataTableRow({
1747
1899
  const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
1748
1900
  const visibleCells = row.getVisibleCells();
1749
1901
  const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
1902
+ row.getIsCellDragSelected = (columnId) => {
1903
+ if (!activeSelectionBounds) return false;
1904
+ if (columnId) {
1905
+ const colIndex = columnIdsByIndex.indexOf(columnId);
1906
+ if (colIndex < 0) return false;
1907
+ const { startRow, rowSpan: rowSpan2 } = resolveRowSpanAt(
1908
+ columnRowSpanMap.get(columnIdsByIndex[colIndex]),
1909
+ rowIndex
1910
+ );
1911
+ return isCellInSelection(
1912
+ startRow,
1913
+ colIndex,
1914
+ activeSelectionBounds,
1915
+ rowSpan2
1916
+ );
1917
+ }
1918
+ for (let colIndex = 0; colIndex < columnIdsByIndex.length; colIndex += 1) {
1919
+ const { startRow, rowSpan: rowSpan2 } = resolveRowSpanAt(
1920
+ columnRowSpanMap.get(columnIdsByIndex[colIndex]),
1921
+ rowIndex
1922
+ );
1923
+ if (isCellInSelection(startRow, colIndex, activeSelectionBounds, rowSpan2)) {
1924
+ return true;
1925
+ }
1926
+ }
1927
+ return false;
1928
+ };
1750
1929
  const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
1751
1930
  if (targetCol < activeSelectionBounds.startCol || targetCol > activeSelectionBounds.endCol) {
1752
1931
  return false;
@@ -1855,6 +2034,17 @@ function DataTableRow({
1855
2034
  );
1856
2035
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
1857
2036
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
2037
+ const sizeStyle = getColumnSizeStyle(cell.column.getSize(), {
2038
+ force: enableColumnResize,
2039
+ lockMax: enableColumnResize
2040
+ });
2041
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(columnId) : void 0;
2042
+ const freezeStyle = getColumnFreezeStyle(freezeOffset);
2043
+ const cellStyle = {
2044
+ ...sizeStyle,
2045
+ ...freezeStyle,
2046
+ ...selectionEdgeStyle
2047
+ };
1858
2048
  return /* @__PURE__ */ jsxs2(
1859
2049
  "td",
1860
2050
  {
@@ -1868,12 +2058,15 @@ function DataTableRow({
1868
2058
  "data-selection-edges": hasSelectionEdges ? "" : void 0,
1869
2059
  "data-editable": editable ? "" : void 0,
1870
2060
  "data-editing": isEditing ? "" : void 0,
2061
+ "data-frozen": freezeOffset?.side,
2062
+ "data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
1871
2063
  onMouseDown: (event) => {
1872
2064
  if (isEditing) {
1873
2065
  event.stopPropagation();
1874
2066
  return;
1875
2067
  }
1876
2068
  if (!enableCellSelection) return;
2069
+ if (isInteractiveMouseTarget(event.target)) return;
1877
2070
  event.preventDefault();
1878
2071
  onCellMouseDown(
1879
2072
  resolveCellRowIndex(event.clientY, event.currentTarget),
@@ -1901,9 +2094,10 @@ function DataTableRow({
1901
2094
  event.stopPropagation();
1902
2095
  onStartEdit(rowIndex, cellIndex);
1903
2096
  },
1904
- style: selectionEdgeStyle,
2097
+ style: Object.keys(cellStyle).length > 0 ? cellStyle : void 0,
1905
2098
  className: cn(
1906
2099
  "data-table-cell",
2100
+ freezeOffset && `data-table-cell--frozen-${freezeOffset.side}`,
1907
2101
  CELL_ALIGN_CLASS[align],
1908
2102
  cellClassName,
1909
2103
  isMerged && "is-merged",
@@ -2136,10 +2330,13 @@ function DataTable({
2136
2330
  rows,
2137
2331
  columnCount,
2138
2332
  selectedCount,
2333
+ labels,
2139
2334
  emptyText,
2140
2335
  loadingText,
2141
2336
  selectionLabel,
2142
2337
  enableCellSelection,
2338
+ enableColumnResize,
2339
+ enableColumnFreeze,
2143
2340
  shouldVirtualize,
2144
2341
  scrollRef,
2145
2342
  rowVirtualizer,
@@ -2154,6 +2351,7 @@ function DataTable({
2154
2351
  const RowSlot = slots?.Row ?? DataTableRow;
2155
2352
  const PendingSlot = slots?.Pending ?? DefaultPending;
2156
2353
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
2354
+ const freezeOffsets = rowContextValue.columnFreeze.offsets;
2157
2355
  const contextValue = useMemo3(
2158
2356
  () => ({ ...rowContextValue, classNames }),
2159
2357
  [rowContextValue, classNames]
@@ -2174,6 +2372,8 @@ function DataTable({
2174
2372
  className: cn(
2175
2373
  "DataTableJSX",
2176
2374
  !enableCellSelection && "DataTableJSX--no-cell-selection",
2375
+ enableColumnResize && "DataTableJSX--column-resize",
2376
+ enableColumnFreeze && "DataTableJSX--column-freeze",
2177
2377
  classNames?.root,
2178
2378
  className
2179
2379
  ),
@@ -2194,6 +2394,7 @@ function DataTable({
2194
2394
  "table",
2195
2395
  {
2196
2396
  className: cn("data-table", classNames?.table),
2397
+ style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
2197
2398
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
2198
2399
  children: [
2199
2400
  /* @__PURE__ */ jsx5("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx5(
@@ -2203,22 +2404,54 @@ function DataTable({
2203
2404
  children: headerGroup.headers.map((header) => {
2204
2405
  const align = header.column.columnDef.meta?.align ?? "center";
2205
2406
  const headerClassName = header.column.columnDef.meta?.headerClassName;
2206
- return /* @__PURE__ */ jsx5(
2407
+ const canResize = enableColumnResize && header.column.getCanResize();
2408
+ const sizeStyle = getColumnSizeStyle(header.getSize(), {
2409
+ force: enableColumnResize,
2410
+ lockMax: enableColumnResize
2411
+ });
2412
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
2413
+ const freezeStyle = getColumnFreezeStyle(freezeOffset, {
2414
+ isHeader: true
2415
+ });
2416
+ const headerStyle = {
2417
+ ...sizeStyle,
2418
+ ...freezeStyle
2419
+ };
2420
+ return /* @__PURE__ */ jsxs4(
2207
2421
  "th",
2208
2422
  {
2209
- style: {
2210
- width: header.getSize() !== 150 ? header.getSize() : void 0,
2211
- // Column sizing follows TanStack's `size`, but
2212
- // ensure the column keeps its min width when the container shrinks.
2213
- minWidth: header.getSize() !== 150 ? header.getSize() : void 0
2214
- },
2423
+ "data-resizing": header.column.getIsResizing() ? "" : void 0,
2424
+ "data-frozen": freezeOffset?.side,
2425
+ "data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
2426
+ style: Object.keys(headerStyle).length > 0 ? headerStyle : void 0,
2215
2427
  className: cn(
2216
2428
  "data-table-head-cell",
2429
+ freezeOffset && `data-table-head-cell--frozen-${freezeOffset.side}`,
2217
2430
  CELL_ALIGN_CLASS[align],
2218
2431
  classNames?.headCell,
2219
2432
  headerClassName
2220
2433
  ),
2221
- children: header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext())
2434
+ children: [
2435
+ header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()),
2436
+ canResize ? /* @__PURE__ */ jsx5(
2437
+ "div",
2438
+ {
2439
+ role: "separator",
2440
+ "aria-orientation": "vertical",
2441
+ "aria-label": labels.resizeColumn,
2442
+ "data-table-disable-cell-selection": "",
2443
+ className: cn(
2444
+ "data-table-resize-handle",
2445
+ classNames?.resizeHandle
2446
+ ),
2447
+ onMouseDown: header.getResizeHandler(),
2448
+ onTouchStart: header.getResizeHandler(),
2449
+ onDoubleClick: () => {
2450
+ header.column.resetSize();
2451
+ }
2452
+ }
2453
+ ) : null
2454
+ ]
2222
2455
  },
2223
2456
  header.id
2224
2457
  );
@@ -2346,6 +2579,10 @@ function buildColumnDef(props, sort, onSort) {
2346
2579
  children,
2347
2580
  sortable = false,
2348
2581
  width,
2582
+ minWidth,
2583
+ maxWidth,
2584
+ resizable,
2585
+ frozen,
2349
2586
  align,
2350
2587
  rowSpan,
2351
2588
  rowSpanKey,
@@ -2359,7 +2596,10 @@ function buildColumnDef(props, sort, onSort) {
2359
2596
  return {
2360
2597
  id: field,
2361
2598
  ...!virtual ? { accessorKey: field } : {},
2362
- size: width ?? 150,
2599
+ size: width ?? DATA_TABLE_COLUMN_SIZE,
2600
+ ...minWidth != null ? { minSize: minWidth } : {},
2601
+ ...maxWidth != null ? { maxSize: maxWidth } : {},
2602
+ ...resizable === false ? { enableResizing: false } : {},
2363
2603
  header: sortable ? () => /* @__PURE__ */ jsx6(SortableHeader, { label: children, field, sort, onSort }) : (
2364
2604
  // eslint-disable-next-line @typescript-eslint/promise-function-async
2365
2605
  () => children
@@ -2379,6 +2619,7 @@ function buildColumnDef(props, sort, onSort) {
2379
2619
  editable,
2380
2620
  editType,
2381
2621
  editInputProps,
2622
+ frozen,
2382
2623
  className,
2383
2624
  headerClassName
2384
2625
  }
@@ -2654,6 +2895,7 @@ export {
2654
2895
  applyCellEdit,
2655
2896
  applyFillData,
2656
2897
  applySelectionUpdater,
2898
+ buildColumnFreezeOffsets,
2657
2899
  buildColumnRowSpanMap,
2658
2900
  buildRowsPastePayload,
2659
2901
  canExpandRow,
@@ -2666,6 +2908,8 @@ export {
2666
2908
  getCellEditDraftValue,
2667
2909
  getCellSelectionEdgeStyle,
2668
2910
  getColumnEditType,
2911
+ getColumnFreezeStyle,
2912
+ getColumnSizeStyle,
2669
2913
  getRowIndexInMergedCell,
2670
2914
  hasCellSelectionEdges,
2671
2915
  isCellInSelection,
@@ -2675,6 +2919,7 @@ export {
2675
2919
  parseCellEditValue,
2676
2920
  parseClipboardTSV,
2677
2921
  parseClipboardTSVWithDepths,
2922
+ resolveColumnFreezeSide,
2678
2923
  resolveDataTableLabels,
2679
2924
  resolvePasteColumnIds,
2680
2925
  resolveRowSelection,
@@ -1,5 +1,42 @@
1
- import { ColumnDef, RowSelectionState, Updater, Row } from '@tanstack/react-table';
2
- import { ReactNode, InputHTMLAttributes, ComponentType } from 'react';
1
+ import { ColumnDef, RowSelectionState, Updater, ColumnSizingState, OnChangeFn, ColumnResizeMode, Row } from '@tanstack/react-table';
2
+ import { CSSProperties, ReactNode, InputHTMLAttributes, ComponentType } from 'react';
3
+
4
+ /** Sticky edge for a frozen column. Does not reorder columns. */
5
+ type ColumnFreezeSide = "left" | "right";
6
+ /**
7
+ * Per-column freeze flag.
8
+ * `true` is sugar for `"left"`.
9
+ */
10
+ type ColumnFreezeMeta = boolean | ColumnFreezeSide;
11
+ type ColumnFreezeColumnInput = {
12
+ id: string;
13
+ size: number;
14
+ side?: ColumnFreezeSide;
15
+ };
16
+ type ColumnFreezeOffset = {
17
+ side: ColumnFreezeSide;
18
+ /** Cumulative sticky inset (`left` or `right` in px). */
19
+ offset: number;
20
+ /** Inner edge of the sticky group (shadow / divider). */
21
+ isEdge: boolean;
22
+ /** Relative stack order within the freeze side (higher = closer to the viewport edge). */
23
+ stack: number;
24
+ };
25
+ /** Normalize column `frozen` meta into a sticky side. */
26
+ declare function resolveColumnFreezeSide(frozen?: ColumnFreezeMeta): ColumnFreezeSide | undefined;
27
+ /**
28
+ * Build sticky insets for frozen columns without changing DOM/column order.
29
+ *
30
+ * - `left`: `left = Σ(widths of left-frozen columns before this one)`
31
+ * - `right`: `right = Σ(widths of right-frozen columns after this one)`
32
+ *
33
+ * Middle columns may be frozen; non-frozen neighbors scroll underneath.
34
+ */
35
+ declare function buildColumnFreezeOffsets(columns: ColumnFreezeColumnInput[]): Map<string, ColumnFreezeOffset>;
36
+ /** Sticky position styles for a frozen header or body cell. */
37
+ declare function getColumnFreezeStyle(offset: ColumnFreezeOffset | undefined, options?: {
38
+ isHeader?: boolean;
39
+ }): CSSProperties | undefined;
3
40
 
4
41
  type DataTableLabels = {
5
42
  empty: string;
@@ -7,6 +44,8 @@ type DataTableLabels = {
7
44
  selection: (selectedCount: number) => ReactNode;
8
45
  expandRow: string;
9
46
  collapseRow: string;
47
+ /** Accessible label for the column resize handle */
48
+ resizeColumn: string;
10
49
  };
11
50
  /** Default copy. Override via the `labels` option. */
12
51
  declare const DEFAULT_DATA_TABLE_LABELS: DataTableLabels;
@@ -31,6 +70,21 @@ declare module "@tanstack/react-table" {
31
70
  editType?: CellEditType;
32
71
  /** Inline editor input attribute overrides */
33
72
  editInputProps?: DataTableEditInputProps;
73
+ /**
74
+ * Freeze (sticky) this column without reordering.
75
+ * `true` / `"left"` stick to the scrollport left; `"right"` to the right.
76
+ * Middle columns are allowed; multiple frozen columns stack via cumulative offsets.
77
+ */
78
+ frozen?: ColumnFreezeMeta;
79
+ }
80
+ interface Row<TData> {
81
+ /**
82
+ * Returns whether this row's cell is inside the active drag selection.
83
+ * Attached at render time by DataTableRow (optional outside that renderer).
84
+ * - `columnId` omitted: true when any visible cell in the row is selected.
85
+ * - `columnId` provided: checks only that column (row-span aware via merge origin).
86
+ */
87
+ getIsCellDragSelected?: (columnId?: string) => boolean;
34
88
  }
35
89
  }
36
90
  type DataTableCopyActions = {
@@ -171,6 +225,25 @@ type DataTableProps<T extends Record<string, unknown>> = {
171
225
  estimateRowHeight?: number;
172
226
  /** Virtualization overscan row count. Default 8 */
173
227
  virtualOverscan?: number;
228
+ /**
229
+ * Enable drag-to-resize on column headers. Defaults to false.
230
+ * Opt out per column with `Column.resizable={false}` / `enableResizing: false`.
231
+ */
232
+ enableColumnResize?: boolean;
233
+ /** Controlled column sizing map (`{ [columnId]: widthPx }`) */
234
+ columnSizing?: ColumnSizingState;
235
+ onColumnSizingChange?: OnChangeFn<ColumnSizingState>;
236
+ /**
237
+ * When to commit resize updates. Defaults to `"onChange"` (live while dragging).
238
+ * Use `"onEnd"` to update only after the pointer is released.
239
+ */
240
+ columnResizeMode?: ColumnResizeMode;
241
+ /**
242
+ * Enable sticky freeze columns. Defaults to false.
243
+ * Mark columns with `Column.frozen` / `meta.frozen` (`true` | `"left"` | `"right"`).
244
+ * Does not reorder columns; offsets stack so frozen cells do not overlap.
245
+ */
246
+ enableColumnFreeze?: boolean;
174
247
  /**
175
248
  * Optional UI part replacements for the unstyled DataTable renderer.
176
249
  * Use with `createTable` / `Table.Column`, or pass columns directly to `DataTable`.
@@ -196,6 +269,8 @@ type DataTableClassNames = {
196
269
  head?: string;
197
270
  headRow?: string;
198
271
  headCell?: string;
272
+ /** Column resize drag handle inside a header cell */
273
+ resizeHandle?: string;
199
274
  body?: string;
200
275
  emptyCell?: string;
201
276
  virtualSpacer?: string;
@@ -257,7 +332,23 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
257
332
  virtual?: boolean;
258
333
  children?: ReactNode;
259
334
  sortable?: boolean;
335
+ /** Initial / default column width in px (TanStack `size`) */
260
336
  width?: number;
337
+ /** Minimum resize width in px. Defaults to the table min when omitted */
338
+ minWidth?: number;
339
+ /** Maximum resize width in px. Defaults to the table max when omitted */
340
+ maxWidth?: number;
341
+ /**
342
+ * When false, this column cannot be resized even if `enableColumnResize` is on.
343
+ * Defaults to true.
344
+ */
345
+ resizable?: boolean;
346
+ /**
347
+ * Freeze (sticky) without changing column order.
348
+ * `true` / `"left"` → left edge; `"right"` → right edge.
349
+ * Requires `enableColumnFreeze`. Middle columns are allowed.
350
+ */
351
+ frozen?: ColumnFreezeMeta;
261
352
  align?: "left" | "center" | "right";
262
353
  rowSpan?: boolean;
263
354
  rowSpanKey?: string;
@@ -273,4 +364,4 @@ type TableProps<T extends Record<string, unknown>> = Omit<DataTableProps<T>, "co
273
364
  children: ReactNode;
274
365
  };
275
366
 
276
- export { type CellEditType as C, DEFAULT_DATA_TABLE_LABELS as D, type PasteMode as P, type RowSelectionMode as R, type TableColumnProps as T, type DataTableClassNames as a, type DataTableCopyActions as b, type DataTableLabels as c, type DataTableProps as d, type DataTableSlots as e, type RowsPastePayload as f, type TableProps as g, resolveDataTableLabels as r };
367
+ export { type ColumnFreezeColumnInput as C, DEFAULT_DATA_TABLE_LABELS as D, type PasteMode as P, type RowSelectionMode as R, type TableColumnProps as T, type ColumnFreezeMeta as a, type ColumnFreezeOffset as b, type ColumnFreezeSide as c, type DataTableClassNames as d, type DataTableCopyActions as e, type DataTableLabels as f, type DataTableProps as g, type DataTableSlots as h, type RowsPastePayload as i, type TableProps as j, buildColumnFreezeOffsets as k, getColumnFreezeStyle as l, resolveDataTableLabels as m, type CellEditType as n, resolveColumnFreezeSide as r };