react-glide-table 1.1.9 → 1.3.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,102 @@ 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 getColumnFreezeEdgeAttr(offset) {
372
+ if (!offset) return void 0;
373
+ if (offset.edgeLeft && offset.edgeRight) return "both";
374
+ if (offset.edgeLeft) return "left";
375
+ if (offset.edgeRight) return "right";
376
+ return void 0;
377
+ }
378
+ function buildColumnFreezeOffsets(columns) {
379
+ const result = /* @__PURE__ */ new Map();
380
+ let leftOffset = 0;
381
+ const leftIds = [];
382
+ for (const column of columns) {
383
+ if (column.side !== "left") continue;
384
+ leftIds.push(column.id);
385
+ result.set(column.id, {
386
+ side: "left",
387
+ offset: leftOffset,
388
+ isEdge: false,
389
+ edgeLeft: false,
390
+ edgeRight: false,
391
+ stack: 0
392
+ });
393
+ leftOffset += column.size;
394
+ }
395
+ leftIds.forEach((id, index) => {
396
+ const entry = result.get(id);
397
+ if (!entry) return;
398
+ entry.stack = leftIds.length - index;
399
+ });
400
+ let rightOffset = 0;
401
+ const rightIds = [];
402
+ for (let index = columns.length - 1; index >= 0; index -= 1) {
403
+ const column = columns[index];
404
+ if (!column || column.side !== "right") continue;
405
+ rightIds.push(column.id);
406
+ result.set(column.id, {
407
+ side: "right",
408
+ offset: rightOffset,
409
+ isEdge: false,
410
+ edgeLeft: false,
411
+ edgeRight: false,
412
+ stack: 0
413
+ });
414
+ rightOffset += column.size;
415
+ }
416
+ rightIds.forEach((id, index) => {
417
+ const entry = result.get(id);
418
+ if (!entry) return;
419
+ entry.stack = rightIds.length - index;
420
+ });
421
+ columns.forEach((column, index) => {
422
+ if (!column.side) return;
423
+ const entry = result.get(column.id);
424
+ if (!entry) return;
425
+ const prevSide = index > 0 ? columns[index - 1]?.side : void 0;
426
+ const nextSide = index < columns.length - 1 ? columns[index + 1]?.side : void 0;
427
+ entry.edgeLeft = prevSide !== column.side;
428
+ entry.edgeRight = nextSide !== column.side;
429
+ if (index === 0) entry.edgeLeft = false;
430
+ if (index === columns.length - 1) entry.edgeRight = false;
431
+ entry.isEdge = entry.edgeLeft || entry.edgeRight;
432
+ });
433
+ return result;
434
+ }
435
+ function getColumnFreezeStyle(offset, options) {
436
+ if (!offset) return void 0;
437
+ const zBase = options?.isHeader ? HEADER_Z_BASE : BODY_Z_BASE;
438
+ return {
439
+ position: "sticky",
440
+ ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
441
+ zIndex: zBase + offset.stack,
442
+ ...options?.isHeader ? { top: 0 } : {}
443
+ };
444
+ }
445
+
446
+ // src/components/ui/table/features/column-resize/columnResize.ts
447
+ function getColumnSizeStyle(size, options) {
448
+ const { force = false, lockMax = false } = options ?? {};
449
+ if (!force && size === DATA_TABLE_COLUMN_SIZE) {
450
+ return void 0;
451
+ }
452
+ return {
453
+ width: size,
454
+ minWidth: size,
455
+ ...lockMax ? { maxWidth: size } : {}
456
+ };
457
+ }
458
+
360
459
  // src/components/ui/table/features/row-expand/row-expand.ts
361
460
  import { useEffect, useMemo, useRef } from "react";
362
461
 
@@ -794,7 +893,18 @@ function DataTableRow({
794
893
  virtualIndex,
795
894
  measureElement
796
895
  }) {
797
- const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
896
+ const {
897
+ classNames,
898
+ rowSpan,
899
+ selection,
900
+ cellSelection,
901
+ cellEdit,
902
+ expand,
903
+ columnResize,
904
+ columnFreeze
905
+ } = useDataTableRowContext();
906
+ const { enableColumnResize } = columnResize;
907
+ const { enableColumnFreeze, offsets: freezeOffsets } = columnFreeze;
798
908
  const {
799
909
  enableRowSpan,
800
910
  primaryRowSpanColumnId,
@@ -975,6 +1085,17 @@ function DataTableRow({
975
1085
  );
976
1086
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
977
1087
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
1088
+ const sizeStyle = getColumnSizeStyle(cell.column.getSize(), {
1089
+ force: enableColumnResize,
1090
+ lockMax: enableColumnResize
1091
+ });
1092
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(columnId) : void 0;
1093
+ const freezeStyle = getColumnFreezeStyle(freezeOffset);
1094
+ const cellStyle = {
1095
+ ...sizeStyle,
1096
+ ...freezeStyle,
1097
+ ...selectionEdgeStyle
1098
+ };
978
1099
  return /* @__PURE__ */ jsxs2(
979
1100
  "td",
980
1101
  {
@@ -988,6 +1109,8 @@ function DataTableRow({
988
1109
  "data-selection-edges": hasSelectionEdges ? "" : void 0,
989
1110
  "data-editable": editable ? "" : void 0,
990
1111
  "data-editing": isEditing ? "" : void 0,
1112
+ "data-frozen": freezeOffset?.side,
1113
+ "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
991
1114
  onMouseDown: (event) => {
992
1115
  if (isEditing) {
993
1116
  event.stopPropagation();
@@ -1022,9 +1145,10 @@ function DataTableRow({
1022
1145
  event.stopPropagation();
1023
1146
  onStartEdit(rowIndex, cellIndex);
1024
1147
  },
1025
- style: selectionEdgeStyle,
1148
+ style: Object.keys(cellStyle).length > 0 ? cellStyle : void 0,
1026
1149
  className: cn(
1027
1150
  "data-table-cell",
1151
+ freezeOffset && `data-table-cell--frozen-${freezeOffset.side}`,
1028
1152
  CELL_ALIGN_CLASS[align],
1029
1153
  cellClassName,
1030
1154
  isMerged && "is-merged",
@@ -1786,7 +1910,8 @@ var DEFAULT_DATA_TABLE_LABELS = {
1786
1910
  loading: "Loading...",
1787
1911
  selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
1788
1912
  expandRow: "Expand row",
1789
- collapseRow: "Collapse row"
1913
+ collapseRow: "Collapse row",
1914
+ resizeColumn: "Resize column"
1790
1915
  };
1791
1916
  function resolveDataTableLabels(partial) {
1792
1917
  return {
@@ -1796,6 +1921,7 @@ function resolveDataTableLabels(partial) {
1796
1921
  }
1797
1922
 
1798
1923
  // src/core/useGlideTable.ts
1924
+ var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
1799
1925
  function useGlideTable(options) {
1800
1926
  const {
1801
1927
  data,
@@ -1831,7 +1957,12 @@ function useGlideTable(options) {
1831
1957
  enableInsertPaste,
1832
1958
  enableVirtualization = true,
1833
1959
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
1834
- virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
1960
+ virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN,
1961
+ enableColumnResize = false,
1962
+ columnSizing: controlledColumnSizing,
1963
+ onColumnSizingChange,
1964
+ columnResizeMode = "onChange",
1965
+ enableColumnFreeze = false
1835
1966
  } = options;
1836
1967
  const labels = useMemo2(() => {
1837
1968
  const resolved = resolveDataTableLabels(labelsProp);
@@ -1845,6 +1976,7 @@ function useGlideTable(options) {
1845
1976
  const enableExpand = Boolean(toggleField);
1846
1977
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1847
1978
  const [internalRowSelection, setInternalRowSelection] = useState3({});
1979
+ const [internalColumnSizing, setInternalColumnSizing] = useState3({});
1848
1980
  const [internalExpandedRows, setInternalExpandedRows] = useState3(
1849
1981
  () => /* @__PURE__ */ new Set()
1850
1982
  );
@@ -1863,6 +1995,7 @@ function useGlideTable(options) {
1863
1995
  controlledRowSelection,
1864
1996
  internalRowSelection
1865
1997
  );
1998
+ const columnSizing = controlledColumnSizing ?? internalColumnSizing;
1866
1999
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
1867
2000
  const handleExpandedRowsChange = useCallback3(
1868
2001
  (next) => {
@@ -1888,9 +2021,27 @@ function useGlideTable(options) {
1888
2021
  const table = useReactTable({
1889
2022
  data: tableData,
1890
2023
  columns,
2024
+ ...enableColumnResize ? {
2025
+ defaultColumn: {
2026
+ minSize: DATA_TABLE_COLUMN_MIN_SIZE,
2027
+ maxSize: DATA_TABLE_COLUMN_MAX_SIZE
2028
+ }
2029
+ } : {},
2030
+ enableColumnResizing: enableColumnResize,
2031
+ columnResizeMode,
1891
2032
  state: {
1892
- rowSelection: rowSelectionMode === "none" ? {} : rowSelection
2033
+ rowSelection: rowSelectionMode === "none" ? {} : rowSelection,
2034
+ ...enableColumnResize ? { columnSizing } : {}
1893
2035
  },
2036
+ onColumnSizingChange: enableColumnResize ? (updater) => {
2037
+ if (onColumnSizingChange) {
2038
+ onColumnSizingChange(updater);
2039
+ return;
2040
+ }
2041
+ setInternalColumnSizing(
2042
+ (previous) => typeof updater === "function" ? updater(previous) : updater
2043
+ );
2044
+ } : void 0,
1894
2045
  enableRowSelection: rowSelectionMode === "none" ? false : getRowCanSelect ? (row) => getRowCanSelect(row.original, row.index) : true,
1895
2046
  enableMultiRowSelection: rowSelectionMode === "multi",
1896
2047
  onRowSelectionChange: (updater) => {
@@ -1921,6 +2072,17 @@ function useGlideTable(options) {
1921
2072
  const selectedCount = selectedRows.length;
1922
2073
  const rows = table.getRowModel().rows;
1923
2074
  const columnCount = table.getAllLeafColumns().length || 1;
2075
+ const visibleLeafColumns = table.getVisibleLeafColumns();
2076
+ const columnFreezeOffsets = useMemo2(() => {
2077
+ if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
2078
+ return buildColumnFreezeOffsets(
2079
+ visibleLeafColumns.map((column) => ({
2080
+ id: column.id,
2081
+ size: column.getSize(),
2082
+ side: resolveColumnFreezeSide(column.columnDef.meta?.frozen)
2083
+ }))
2084
+ );
2085
+ }, [enableColumnFreeze, visibleLeafColumns, columnSizing]);
1924
2086
  const rowVirtualizer = useVirtualizer({
1925
2087
  count: shouldVirtualize ? rows.length : 0,
1926
2088
  getScrollElement: () => scrollRef.current,
@@ -2037,6 +2199,13 @@ function useGlideTable(options) {
2037
2199
  onToggleExpand: handleToggleExpand,
2038
2200
  expandRowLabel: labels.expandRow,
2039
2201
  collapseRowLabel: labels.collapseRow
2202
+ },
2203
+ columnResize: {
2204
+ enableColumnResize
2205
+ },
2206
+ columnFreeze: {
2207
+ enableColumnFreeze,
2208
+ offsets: columnFreezeOffsets
2040
2209
  }
2041
2210
  };
2042
2211
  }, [
@@ -2069,7 +2238,10 @@ function useGlideTable(options) {
2069
2238
  preventExpand,
2070
2239
  handleToggleExpand,
2071
2240
  labels.expandRow,
2072
- labels.collapseRow
2241
+ labels.collapseRow,
2242
+ enableColumnResize,
2243
+ enableColumnFreeze,
2244
+ columnFreezeOffsets
2073
2245
  ]);
2074
2246
  const copySelectionRef = useRef5(copySelection);
2075
2247
  useEffect5(() => {
@@ -2090,6 +2262,8 @@ function useGlideTable(options) {
2090
2262
  loadingText: labels.loading,
2091
2263
  selectionLabel: labels.selection,
2092
2264
  enableCellSelection,
2265
+ enableColumnResize,
2266
+ enableColumnFreeze,
2093
2267
  shouldVirtualize,
2094
2268
  scrollRef,
2095
2269
  rowVirtualizer,
@@ -2155,10 +2329,13 @@ function DataTable({
2155
2329
  rows,
2156
2330
  columnCount,
2157
2331
  selectedCount,
2332
+ labels,
2158
2333
  emptyText,
2159
2334
  loadingText,
2160
2335
  selectionLabel,
2161
2336
  enableCellSelection,
2337
+ enableColumnResize,
2338
+ enableColumnFreeze,
2162
2339
  shouldVirtualize,
2163
2340
  scrollRef,
2164
2341
  rowVirtualizer,
@@ -2173,6 +2350,7 @@ function DataTable({
2173
2350
  const RowSlot = slots?.Row ?? DataTableRow;
2174
2351
  const PendingSlot = slots?.Pending ?? DefaultPending;
2175
2352
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
2353
+ const freezeOffsets = rowContextValue.columnFreeze.offsets;
2176
2354
  const contextValue = useMemo3(
2177
2355
  () => ({ ...rowContextValue, classNames }),
2178
2356
  [rowContextValue, classNames]
@@ -2193,6 +2371,8 @@ function DataTable({
2193
2371
  className: cn(
2194
2372
  "DataTableJSX",
2195
2373
  !enableCellSelection && "DataTableJSX--no-cell-selection",
2374
+ enableColumnResize && "DataTableJSX--column-resize",
2375
+ enableColumnFreeze && "DataTableJSX--column-freeze",
2196
2376
  classNames?.root,
2197
2377
  className
2198
2378
  ),
@@ -2213,6 +2393,7 @@ function DataTable({
2213
2393
  "table",
2214
2394
  {
2215
2395
  className: cn("data-table", classNames?.table),
2396
+ style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
2216
2397
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
2217
2398
  children: [
2218
2399
  /* @__PURE__ */ jsx5("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx5(
@@ -2222,22 +2403,54 @@ function DataTable({
2222
2403
  children: headerGroup.headers.map((header) => {
2223
2404
  const align = header.column.columnDef.meta?.align ?? "center";
2224
2405
  const headerClassName = header.column.columnDef.meta?.headerClassName;
2225
- return /* @__PURE__ */ jsx5(
2406
+ const canResize = enableColumnResize && header.column.getCanResize();
2407
+ const sizeStyle = getColumnSizeStyle(header.getSize(), {
2408
+ force: enableColumnResize,
2409
+ lockMax: enableColumnResize
2410
+ });
2411
+ const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
2412
+ const freezeStyle = getColumnFreezeStyle(freezeOffset, {
2413
+ isHeader: true
2414
+ });
2415
+ const headerStyle = {
2416
+ ...sizeStyle,
2417
+ ...freezeStyle
2418
+ };
2419
+ return /* @__PURE__ */ jsxs4(
2226
2420
  "th",
2227
2421
  {
2228
- style: {
2229
- width: header.getSize() !== 150 ? header.getSize() : void 0,
2230
- // Column sizing follows TanStack's `size`, but
2231
- // ensure the column keeps its min width when the container shrinks.
2232
- minWidth: header.getSize() !== 150 ? header.getSize() : void 0
2233
- },
2422
+ "data-resizing": header.column.getIsResizing() ? "" : void 0,
2423
+ "data-frozen": freezeOffset?.side,
2424
+ "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
2425
+ style: Object.keys(headerStyle).length > 0 ? headerStyle : void 0,
2234
2426
  className: cn(
2235
2427
  "data-table-head-cell",
2428
+ freezeOffset && `data-table-head-cell--frozen-${freezeOffset.side}`,
2236
2429
  CELL_ALIGN_CLASS[align],
2237
2430
  classNames?.headCell,
2238
2431
  headerClassName
2239
2432
  ),
2240
- children: header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext())
2433
+ children: [
2434
+ header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()),
2435
+ canResize ? /* @__PURE__ */ jsx5(
2436
+ "div",
2437
+ {
2438
+ role: "separator",
2439
+ "aria-orientation": "vertical",
2440
+ "aria-label": labels.resizeColumn,
2441
+ "data-table-disable-cell-selection": "",
2442
+ className: cn(
2443
+ "data-table-resize-handle",
2444
+ classNames?.resizeHandle
2445
+ ),
2446
+ onMouseDown: header.getResizeHandler(),
2447
+ onTouchStart: header.getResizeHandler(),
2448
+ onDoubleClick: () => {
2449
+ header.column.resetSize();
2450
+ }
2451
+ }
2452
+ ) : null
2453
+ ]
2241
2454
  },
2242
2455
  header.id
2243
2456
  );
@@ -2365,6 +2578,10 @@ function buildColumnDef(props, sort, onSort) {
2365
2578
  children,
2366
2579
  sortable = false,
2367
2580
  width,
2581
+ minWidth,
2582
+ maxWidth,
2583
+ resizable,
2584
+ frozen,
2368
2585
  align,
2369
2586
  rowSpan,
2370
2587
  rowSpanKey,
@@ -2378,7 +2595,10 @@ function buildColumnDef(props, sort, onSort) {
2378
2595
  return {
2379
2596
  id: field,
2380
2597
  ...!virtual ? { accessorKey: field } : {},
2381
- size: width ?? 150,
2598
+ size: width ?? DATA_TABLE_COLUMN_SIZE,
2599
+ ...minWidth != null ? { minSize: minWidth } : {},
2600
+ ...maxWidth != null ? { maxSize: maxWidth } : {},
2601
+ ...resizable === false ? { enableResizing: false } : {},
2382
2602
  header: sortable ? () => /* @__PURE__ */ jsx6(SortableHeader, { label: children, field, sort, onSort }) : (
2383
2603
  // eslint-disable-next-line @typescript-eslint/promise-function-async
2384
2604
  () => children
@@ -2398,6 +2618,7 @@ function buildColumnDef(props, sort, onSort) {
2398
2618
  editable,
2399
2619
  editType,
2400
2620
  editInputProps,
2621
+ frozen,
2401
2622
  className,
2402
2623
  headerClassName
2403
2624
  }