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/compound.js CHANGED
@@ -17,6 +17,9 @@ var ROW_HOVERED_BG_CLASS = "row-hovered";
17
17
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
18
18
  var DATA_TABLE_ROW_HEIGHT = 44;
19
19
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
20
+ var DATA_TABLE_COLUMN_SIZE = 150;
21
+ var DATA_TABLE_COLUMN_MIN_SIZE = 40;
22
+ var DATA_TABLE_COLUMN_MAX_SIZE = 800;
20
23
 
21
24
  // src/components/ui/table/DataTableContext.tsx
22
25
  import { createContext, use } from "react";
@@ -357,6 +360,81 @@ function hasCellSelectionEdges(style) {
357
360
  );
358
361
  }
359
362
 
363
+ // src/components/ui/table/features/column-freeze/columnFreeze.ts
364
+ var HEADER_Z_BASE = 30;
365
+ var BODY_Z_BASE = 5;
366
+ function resolveColumnFreezeSide(frozen) {
367
+ if (frozen === true || frozen === "left") return "left";
368
+ if (frozen === "right") return "right";
369
+ return void 0;
370
+ }
371
+ function buildColumnFreezeOffsets(columns) {
372
+ const result = /* @__PURE__ */ new Map();
373
+ let leftOffset = 0;
374
+ const leftIds = [];
375
+ for (const column of columns) {
376
+ if (column.side !== "left") continue;
377
+ leftIds.push(column.id);
378
+ result.set(column.id, {
379
+ side: "left",
380
+ offset: leftOffset,
381
+ isEdge: false,
382
+ stack: 0
383
+ });
384
+ leftOffset += column.size;
385
+ }
386
+ leftIds.forEach((id, index) => {
387
+ const entry = result.get(id);
388
+ if (!entry) return;
389
+ entry.isEdge = index === leftIds.length - 1;
390
+ entry.stack = leftIds.length - index;
391
+ });
392
+ let rightOffset = 0;
393
+ const rightIds = [];
394
+ for (let index = columns.length - 1; index >= 0; index -= 1) {
395
+ const column = columns[index];
396
+ if (!column || column.side !== "right") continue;
397
+ rightIds.push(column.id);
398
+ result.set(column.id, {
399
+ side: "right",
400
+ offset: rightOffset,
401
+ isEdge: false,
402
+ stack: 0
403
+ });
404
+ rightOffset += column.size;
405
+ }
406
+ rightIds.forEach((id, index) => {
407
+ const entry = result.get(id);
408
+ if (!entry) return;
409
+ entry.isEdge = index === rightIds.length - 1;
410
+ entry.stack = rightIds.length - index;
411
+ });
412
+ return result;
413
+ }
414
+ function getColumnFreezeStyle(offset, options) {
415
+ if (!offset) return void 0;
416
+ const zBase = options?.isHeader ? HEADER_Z_BASE : BODY_Z_BASE;
417
+ return {
418
+ position: "sticky",
419
+ ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
420
+ zIndex: zBase + offset.stack,
421
+ ...options?.isHeader ? { top: 0 } : {}
422
+ };
423
+ }
424
+
425
+ // src/components/ui/table/features/column-resize/columnResize.ts
426
+ function getColumnSizeStyle(size, options) {
427
+ const { force = false, lockMax = false } = options ?? {};
428
+ if (!force && size === DATA_TABLE_COLUMN_SIZE) {
429
+ return void 0;
430
+ }
431
+ return {
432
+ width: size,
433
+ minWidth: size,
434
+ ...lockMax ? { maxWidth: size } : {}
435
+ };
436
+ }
437
+
360
438
  // src/components/ui/table/features/row-expand/row-expand.ts
361
439
  import { useEffect, useMemo, useRef } from "react";
362
440
 
@@ -761,6 +839,19 @@ function cn(...inputs) {
761
839
 
762
840
  // src/components/ui/table/components/DataTable/DataTableRow.tsx
763
841
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
842
+ function isInteractiveMouseTarget(target) {
843
+ if (!(target instanceof Element)) return false;
844
+ const interactiveSelector = [
845
+ "input",
846
+ "textarea",
847
+ "select",
848
+ "button",
849
+ "a[href]",
850
+ "[contenteditable]:not([contenteditable='false'])",
851
+ "[data-table-disable-cell-selection]"
852
+ ].join(",");
853
+ return target.closest(interactiveSelector) !== null;
854
+ }
764
855
  function resolveExpandCellIndex(cells, toggleField) {
765
856
  if (!toggleField) return 0;
766
857
  const matchedIndex = cells.findIndex(
@@ -781,7 +872,18 @@ function DataTableRow({
781
872
  virtualIndex,
782
873
  measureElement
783
874
  }) {
784
- const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
875
+ const {
876
+ classNames,
877
+ rowSpan,
878
+ selection,
879
+ cellSelection,
880
+ cellEdit,
881
+ expand,
882
+ columnResize,
883
+ columnFreeze
884
+ } = useDataTableRowContext();
885
+ const { enableColumnResize } = columnResize;
886
+ const { enableColumnFreeze, offsets: freezeOffsets } = columnFreeze;
785
887
  const {
786
888
  enableRowSpan,
787
889
  primaryRowSpanColumnId,
@@ -827,6 +929,33 @@ function DataTableRow({
827
929
  const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
828
930
  const visibleCells = row.getVisibleCells();
829
931
  const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
932
+ row.getIsCellDragSelected = (columnId) => {
933
+ if (!activeSelectionBounds) return false;
934
+ if (columnId) {
935
+ const colIndex = columnIdsByIndex.indexOf(columnId);
936
+ if (colIndex < 0) return false;
937
+ const { startRow, rowSpan: rowSpan2 } = resolveRowSpanAt(
938
+ columnRowSpanMap.get(columnIdsByIndex[colIndex]),
939
+ rowIndex
940
+ );
941
+ return isCellInSelection(
942
+ startRow,
943
+ colIndex,
944
+ activeSelectionBounds,
945
+ rowSpan2
946
+ );
947
+ }
948
+ for (let colIndex = 0; colIndex < columnIdsByIndex.length; colIndex += 1) {
949
+ const { startRow, rowSpan: rowSpan2 } = resolveRowSpanAt(
950
+ columnRowSpanMap.get(columnIdsByIndex[colIndex]),
951
+ rowIndex
952
+ );
953
+ if (isCellInSelection(startRow, colIndex, activeSelectionBounds, rowSpan2)) {
954
+ return true;
955
+ }
956
+ }
957
+ return false;
958
+ };
830
959
  const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
831
960
  if (targetCol < activeSelectionBounds.startCol || targetCol > activeSelectionBounds.endCol) {
832
961
  return false;
@@ -935,6 +1064,17 @@ function DataTableRow({
935
1064
  );
936
1065
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
937
1066
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
1067
+ const sizeStyle = getColumnSizeStyle(cell.column.getSize(), {
1068
+ force: enableColumnResize,
1069
+ lockMax: enableColumnResize
1070
+ });
1071
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(columnId) : void 0;
1072
+ const freezeStyle = getColumnFreezeStyle(freezeOffset);
1073
+ const cellStyle = {
1074
+ ...sizeStyle,
1075
+ ...freezeStyle,
1076
+ ...selectionEdgeStyle
1077
+ };
938
1078
  return /* @__PURE__ */ jsxs2(
939
1079
  "td",
940
1080
  {
@@ -948,12 +1088,15 @@ function DataTableRow({
948
1088
  "data-selection-edges": hasSelectionEdges ? "" : void 0,
949
1089
  "data-editable": editable ? "" : void 0,
950
1090
  "data-editing": isEditing ? "" : void 0,
1091
+ "data-frozen": freezeOffset?.side,
1092
+ "data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
951
1093
  onMouseDown: (event) => {
952
1094
  if (isEditing) {
953
1095
  event.stopPropagation();
954
1096
  return;
955
1097
  }
956
1098
  if (!enableCellSelection) return;
1099
+ if (isInteractiveMouseTarget(event.target)) return;
957
1100
  event.preventDefault();
958
1101
  onCellMouseDown(
959
1102
  resolveCellRowIndex(event.clientY, event.currentTarget),
@@ -981,9 +1124,10 @@ function DataTableRow({
981
1124
  event.stopPropagation();
982
1125
  onStartEdit(rowIndex, cellIndex);
983
1126
  },
984
- style: selectionEdgeStyle,
1127
+ style: Object.keys(cellStyle).length > 0 ? cellStyle : void 0,
985
1128
  className: cn(
986
1129
  "data-table-cell",
1130
+ freezeOffset && `data-table-cell--frozen-${freezeOffset.side}`,
987
1131
  CELL_ALIGN_CLASS[align],
988
1132
  cellClassName,
989
1133
  isMerged && "is-merged",
@@ -1745,7 +1889,8 @@ var DEFAULT_DATA_TABLE_LABELS = {
1745
1889
  loading: "Loading...",
1746
1890
  selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
1747
1891
  expandRow: "Expand row",
1748
- collapseRow: "Collapse row"
1892
+ collapseRow: "Collapse row",
1893
+ resizeColumn: "Resize column"
1749
1894
  };
1750
1895
  function resolveDataTableLabels(partial) {
1751
1896
  return {
@@ -1755,6 +1900,7 @@ function resolveDataTableLabels(partial) {
1755
1900
  }
1756
1901
 
1757
1902
  // src/core/useGlideTable.ts
1903
+ var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
1758
1904
  function useGlideTable(options) {
1759
1905
  const {
1760
1906
  data,
@@ -1790,7 +1936,12 @@ function useGlideTable(options) {
1790
1936
  enableInsertPaste,
1791
1937
  enableVirtualization = true,
1792
1938
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
1793
- virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
1939
+ virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN,
1940
+ enableColumnResize = false,
1941
+ columnSizing: controlledColumnSizing,
1942
+ onColumnSizingChange,
1943
+ columnResizeMode = "onChange",
1944
+ enableColumnFreeze = false
1794
1945
  } = options;
1795
1946
  const labels = useMemo2(() => {
1796
1947
  const resolved = resolveDataTableLabels(labelsProp);
@@ -1804,6 +1955,7 @@ function useGlideTable(options) {
1804
1955
  const enableExpand = Boolean(toggleField);
1805
1956
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1806
1957
  const [internalRowSelection, setInternalRowSelection] = useState3({});
1958
+ const [internalColumnSizing, setInternalColumnSizing] = useState3({});
1807
1959
  const [internalExpandedRows, setInternalExpandedRows] = useState3(
1808
1960
  () => /* @__PURE__ */ new Set()
1809
1961
  );
@@ -1822,6 +1974,7 @@ function useGlideTable(options) {
1822
1974
  controlledRowSelection,
1823
1975
  internalRowSelection
1824
1976
  );
1977
+ const columnSizing = controlledColumnSizing ?? internalColumnSizing;
1825
1978
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
1826
1979
  const handleExpandedRowsChange = useCallback3(
1827
1980
  (next) => {
@@ -1847,9 +2000,27 @@ function useGlideTable(options) {
1847
2000
  const table = useReactTable({
1848
2001
  data: tableData,
1849
2002
  columns,
2003
+ ...enableColumnResize ? {
2004
+ defaultColumn: {
2005
+ minSize: DATA_TABLE_COLUMN_MIN_SIZE,
2006
+ maxSize: DATA_TABLE_COLUMN_MAX_SIZE
2007
+ }
2008
+ } : {},
2009
+ enableColumnResizing: enableColumnResize,
2010
+ columnResizeMode,
1850
2011
  state: {
1851
- rowSelection: rowSelectionMode === "none" ? {} : rowSelection
2012
+ rowSelection: rowSelectionMode === "none" ? {} : rowSelection,
2013
+ ...enableColumnResize ? { columnSizing } : {}
1852
2014
  },
2015
+ onColumnSizingChange: enableColumnResize ? (updater) => {
2016
+ if (onColumnSizingChange) {
2017
+ onColumnSizingChange(updater);
2018
+ return;
2019
+ }
2020
+ setInternalColumnSizing(
2021
+ (previous) => typeof updater === "function" ? updater(previous) : updater
2022
+ );
2023
+ } : void 0,
1853
2024
  enableRowSelection: rowSelectionMode === "none" ? false : getRowCanSelect ? (row) => getRowCanSelect(row.original, row.index) : true,
1854
2025
  enableMultiRowSelection: rowSelectionMode === "multi",
1855
2026
  onRowSelectionChange: (updater) => {
@@ -1880,6 +2051,17 @@ function useGlideTable(options) {
1880
2051
  const selectedCount = selectedRows.length;
1881
2052
  const rows = table.getRowModel().rows;
1882
2053
  const columnCount = table.getAllLeafColumns().length || 1;
2054
+ const visibleLeafColumns = table.getVisibleLeafColumns();
2055
+ const columnFreezeOffsets = useMemo2(() => {
2056
+ if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
2057
+ return buildColumnFreezeOffsets(
2058
+ visibleLeafColumns.map((column) => ({
2059
+ id: column.id,
2060
+ size: column.getSize(),
2061
+ side: resolveColumnFreezeSide(column.columnDef.meta?.frozen)
2062
+ }))
2063
+ );
2064
+ }, [enableColumnFreeze, visibleLeafColumns, columnSizing]);
1883
2065
  const rowVirtualizer = useVirtualizer({
1884
2066
  count: shouldVirtualize ? rows.length : 0,
1885
2067
  getScrollElement: () => scrollRef.current,
@@ -1996,6 +2178,13 @@ function useGlideTable(options) {
1996
2178
  onToggleExpand: handleToggleExpand,
1997
2179
  expandRowLabel: labels.expandRow,
1998
2180
  collapseRowLabel: labels.collapseRow
2181
+ },
2182
+ columnResize: {
2183
+ enableColumnResize
2184
+ },
2185
+ columnFreeze: {
2186
+ enableColumnFreeze,
2187
+ offsets: columnFreezeOffsets
1999
2188
  }
2000
2189
  };
2001
2190
  }, [
@@ -2028,7 +2217,10 @@ function useGlideTable(options) {
2028
2217
  preventExpand,
2029
2218
  handleToggleExpand,
2030
2219
  labels.expandRow,
2031
- labels.collapseRow
2220
+ labels.collapseRow,
2221
+ enableColumnResize,
2222
+ enableColumnFreeze,
2223
+ columnFreezeOffsets
2032
2224
  ]);
2033
2225
  const copySelectionRef = useRef5(copySelection);
2034
2226
  useEffect5(() => {
@@ -2049,6 +2241,8 @@ function useGlideTable(options) {
2049
2241
  loadingText: labels.loading,
2050
2242
  selectionLabel: labels.selection,
2051
2243
  enableCellSelection,
2244
+ enableColumnResize,
2245
+ enableColumnFreeze,
2052
2246
  shouldVirtualize,
2053
2247
  scrollRef,
2054
2248
  rowVirtualizer,
@@ -2114,10 +2308,13 @@ function DataTable({
2114
2308
  rows,
2115
2309
  columnCount,
2116
2310
  selectedCount,
2311
+ labels,
2117
2312
  emptyText,
2118
2313
  loadingText,
2119
2314
  selectionLabel,
2120
2315
  enableCellSelection,
2316
+ enableColumnResize,
2317
+ enableColumnFreeze,
2121
2318
  shouldVirtualize,
2122
2319
  scrollRef,
2123
2320
  rowVirtualizer,
@@ -2132,6 +2329,7 @@ function DataTable({
2132
2329
  const RowSlot = slots?.Row ?? DataTableRow;
2133
2330
  const PendingSlot = slots?.Pending ?? DefaultPending;
2134
2331
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
2332
+ const freezeOffsets = rowContextValue.columnFreeze.offsets;
2135
2333
  const contextValue = useMemo3(
2136
2334
  () => ({ ...rowContextValue, classNames }),
2137
2335
  [rowContextValue, classNames]
@@ -2152,6 +2350,8 @@ function DataTable({
2152
2350
  className: cn(
2153
2351
  "DataTableJSX",
2154
2352
  !enableCellSelection && "DataTableJSX--no-cell-selection",
2353
+ enableColumnResize && "DataTableJSX--column-resize",
2354
+ enableColumnFreeze && "DataTableJSX--column-freeze",
2155
2355
  classNames?.root,
2156
2356
  className
2157
2357
  ),
@@ -2172,6 +2372,7 @@ function DataTable({
2172
2372
  "table",
2173
2373
  {
2174
2374
  className: cn("data-table", classNames?.table),
2375
+ style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
2175
2376
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
2176
2377
  children: [
2177
2378
  /* @__PURE__ */ jsx5("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx5(
@@ -2181,22 +2382,54 @@ function DataTable({
2181
2382
  children: headerGroup.headers.map((header) => {
2182
2383
  const align = header.column.columnDef.meta?.align ?? "center";
2183
2384
  const headerClassName = header.column.columnDef.meta?.headerClassName;
2184
- return /* @__PURE__ */ jsx5(
2385
+ const canResize = enableColumnResize && header.column.getCanResize();
2386
+ const sizeStyle = getColumnSizeStyle(header.getSize(), {
2387
+ force: enableColumnResize,
2388
+ lockMax: enableColumnResize
2389
+ });
2390
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
2391
+ const freezeStyle = getColumnFreezeStyle(freezeOffset, {
2392
+ isHeader: true
2393
+ });
2394
+ const headerStyle = {
2395
+ ...sizeStyle,
2396
+ ...freezeStyle
2397
+ };
2398
+ return /* @__PURE__ */ jsxs4(
2185
2399
  "th",
2186
2400
  {
2187
- style: {
2188
- width: header.getSize() !== 150 ? header.getSize() : void 0,
2189
- // Column sizing follows TanStack's `size`, but
2190
- // ensure the column keeps its min width when the container shrinks.
2191
- minWidth: header.getSize() !== 150 ? header.getSize() : void 0
2192
- },
2401
+ "data-resizing": header.column.getIsResizing() ? "" : void 0,
2402
+ "data-frozen": freezeOffset?.side,
2403
+ "data-freeze-edge": freezeOffset?.isEdge ? "" : void 0,
2404
+ style: Object.keys(headerStyle).length > 0 ? headerStyle : void 0,
2193
2405
  className: cn(
2194
2406
  "data-table-head-cell",
2407
+ freezeOffset && `data-table-head-cell--frozen-${freezeOffset.side}`,
2195
2408
  CELL_ALIGN_CLASS[align],
2196
2409
  classNames?.headCell,
2197
2410
  headerClassName
2198
2411
  ),
2199
- children: header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext())
2412
+ children: [
2413
+ header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()),
2414
+ canResize ? /* @__PURE__ */ jsx5(
2415
+ "div",
2416
+ {
2417
+ role: "separator",
2418
+ "aria-orientation": "vertical",
2419
+ "aria-label": labels.resizeColumn,
2420
+ "data-table-disable-cell-selection": "",
2421
+ className: cn(
2422
+ "data-table-resize-handle",
2423
+ classNames?.resizeHandle
2424
+ ),
2425
+ onMouseDown: header.getResizeHandler(),
2426
+ onTouchStart: header.getResizeHandler(),
2427
+ onDoubleClick: () => {
2428
+ header.column.resetSize();
2429
+ }
2430
+ }
2431
+ ) : null
2432
+ ]
2200
2433
  },
2201
2434
  header.id
2202
2435
  );
@@ -2324,6 +2557,10 @@ function buildColumnDef(props, sort, onSort) {
2324
2557
  children,
2325
2558
  sortable = false,
2326
2559
  width,
2560
+ minWidth,
2561
+ maxWidth,
2562
+ resizable,
2563
+ frozen,
2327
2564
  align,
2328
2565
  rowSpan,
2329
2566
  rowSpanKey,
@@ -2337,7 +2574,10 @@ function buildColumnDef(props, sort, onSort) {
2337
2574
  return {
2338
2575
  id: field,
2339
2576
  ...!virtual ? { accessorKey: field } : {},
2340
- size: width ?? 150,
2577
+ size: width ?? DATA_TABLE_COLUMN_SIZE,
2578
+ ...minWidth != null ? { minSize: minWidth } : {},
2579
+ ...maxWidth != null ? { maxSize: maxWidth } : {},
2580
+ ...resizable === false ? { enableResizing: false } : {},
2341
2581
  header: sortable ? () => /* @__PURE__ */ jsx6(SortableHeader, { label: children, field, sort, onSort }) : (
2342
2582
  // eslint-disable-next-line @typescript-eslint/promise-function-async
2343
2583
  () => children
@@ -2357,6 +2597,7 @@ function buildColumnDef(props, sort, onSort) {
2357
2597
  editable,
2358
2598
  editType,
2359
2599
  editInputProps,
2600
+ frozen,
2360
2601
  className,
2361
2602
  headerClassName
2362
2603
  }